mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
perf(serde_v8): zero-copy StringOrBuffer (#14381)
This commit is contained in:
parent
2eb8c3b82f
commit
4b7d306a19
9 changed files with 127 additions and 46 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3568,7 +3568,6 @@ dependencies = [
|
|||
"bencher",
|
||||
"derive_more",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"v8",
|
||||
]
|
||||
|
|
|
@ -38,6 +38,7 @@ use deno_websocket::ws_create_server_stream;
|
|||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use fly_accept_encoding::Encoding;
|
||||
use hyper::body::Bytes;
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::Service;
|
||||
use hyper::Body;
|
||||
|
@ -612,7 +613,7 @@ async fn op_http_write_headers(
|
|||
// (~4MB)
|
||||
let mut writer =
|
||||
brotli::CompressorWriter::new(Vec::new(), 4096, 6, 22);
|
||||
writer.write_all(&data.into_bytes())?;
|
||||
writer.write_all(&data)?;
|
||||
body = builder.body(writer.into_inner().into())?;
|
||||
}
|
||||
_ => {
|
||||
|
@ -623,14 +624,14 @@ async fn op_http_write_headers(
|
|||
// 1.
|
||||
// https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_comp_level
|
||||
let mut writer = GzEncoder::new(Vec::new(), Compression::new(1));
|
||||
writer.write_all(&data.into_bytes())?;
|
||||
writer.write_all(&data)?;
|
||||
body = builder.body(writer.finish()?.into())?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If a buffer was passed, but isn't compressible, we use it to
|
||||
// construct a response body.
|
||||
body = builder.body(data.into_bytes().into())?;
|
||||
body = builder.body(Bytes::copy_from_slice(&data).into())?;
|
||||
}
|
||||
new_wr = HttpResponseWriter::Closed;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ path = "lib.rs"
|
|||
[dependencies]
|
||||
derive_more = "0.99.17"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
v8 = "0.42.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -155,6 +155,54 @@ fn de_bstr_v8_1024_b(b: &mut Bencher) {
|
|||
);
|
||||
}
|
||||
|
||||
fn de_sob_str_6b(b: &mut Bencher) {
|
||||
dedo("'byebye'", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn de_sob_str_1kb(b: &mut Bencher) {
|
||||
dedo("'deno'.repeat(256)", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn de_sob_buf_1b(b: &mut Bencher) {
|
||||
dedo("new Uint8Array([97])", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn de_sob_buf_1kb(b: &mut Bencher) {
|
||||
dedo("(new Uint8Array(1*1024)).fill(42)", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn de_sob_buf_16kb(b: &mut Bencher) {
|
||||
dedo("(new Uint8Array(16*1024)).fill(42)", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn de_sob_buf_512kb(b: &mut Bencher) {
|
||||
dedo("(new Uint8Array(512*1024)).fill(42)", |scope, v| {
|
||||
b.iter(move || {
|
||||
let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
benches,
|
||||
de_struct_v8,
|
||||
|
@ -174,6 +222,12 @@ benchmark_group!(
|
|||
de_tuple_v8_opt,
|
||||
de_bstr_v8_12_b,
|
||||
de_bstr_v8_1024_b,
|
||||
de_sob_str_6b,
|
||||
de_sob_str_1kb,
|
||||
de_sob_buf_1b,
|
||||
de_sob_buf_1kb,
|
||||
de_sob_buf_16kb,
|
||||
de_sob_buf_512kb,
|
||||
);
|
||||
|
||||
benchmark_main!(benches);
|
||||
|
|
|
@ -7,7 +7,9 @@ use crate::keys::{v8_struct_key, KeyCache};
|
|||
use crate::magic::transl8::FromV8;
|
||||
use crate::magic::transl8::{visit_magic, MagicType};
|
||||
use crate::payload::ValueType;
|
||||
use crate::{magic, Buffer, ByteString, DetachedBuffer, U16String};
|
||||
use crate::{
|
||||
magic, Buffer, ByteString, DetachedBuffer, StringOrBuffer, U16String,
|
||||
};
|
||||
|
||||
pub struct Deserializer<'a, 'b, 's> {
|
||||
input: v8::Local<'a, v8::Value>,
|
||||
|
@ -359,6 +361,9 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
|||
U16String::MAGIC_NAME => {
|
||||
visit_magic(visitor, U16String::from_v8(self.scope, self.input)?)
|
||||
}
|
||||
StringOrBuffer::MAGIC_NAME => {
|
||||
visit_magic(visitor, StringOrBuffer::from_v8(self.scope, self.input)?)
|
||||
}
|
||||
magic::Value::MAGIC_NAME => {
|
||||
visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Mutex;
|
||||
|
@ -21,6 +22,12 @@ pub enum MagicBuffer {
|
|||
|
||||
impl_magic!(MagicBuffer);
|
||||
|
||||
impl Debug for MagicBuffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.as_ref().iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl MagicBuffer {
|
||||
pub fn empty() -> Self {
|
||||
MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
|
||||
|
|
|
@ -1,45 +1,50 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
use super::buffer::MagicBuffer;
|
||||
use super::transl8::{FromV8, ToV8};
|
||||
use crate::magic::transl8::impl_magic;
|
||||
use crate::Error;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StringOrBuffer(Vec<u8>);
|
||||
|
||||
impl Deref for StringOrBuffer {
|
||||
type Target = Vec<u8>;
|
||||
fn deref(&self) -> &Vec<u8> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl StringOrBuffer {
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for StringOrBuffer {
|
||||
fn deserialize<D>(deserializer: D) -> Result<StringOrBuffer, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
StringOrBufferInner::deserialize(deserializer)
|
||||
.map(|x| StringOrBuffer(x.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@AaronO): explore if we can make this work with ZeroCopyBuf
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum StringOrBufferInner {
|
||||
#[serde(with = "serde_bytes")]
|
||||
Buffer(Vec<u8>),
|
||||
pub enum StringOrBuffer {
|
||||
Buffer(MagicBuffer),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl StringOrBufferInner {
|
||||
fn into_bytes(self) -> Vec<u8> {
|
||||
impl_magic!(StringOrBuffer);
|
||||
|
||||
impl Deref for StringOrBuffer {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::String(s) => s.into_bytes(),
|
||||
Self::Buffer(b) => b,
|
||||
Self::Buffer(b) => b.as_ref(),
|
||||
Self::String(s) => s.as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToV8 for StringOrBuffer {
|
||||
fn to_v8<'a>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
) -> Result<v8::Local<'a, v8::Value>, crate::Error> {
|
||||
match self {
|
||||
Self::Buffer(buf) => crate::to_v8(scope, buf),
|
||||
Self::String(s) => crate::to_v8(scope, s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromV8 for StringOrBuffer {
|
||||
fn from_v8(
|
||||
scope: &mut v8::HandleScope,
|
||||
value: v8::Local<v8::Value>,
|
||||
) -> Result<Self, crate::Error> {
|
||||
if let Ok(buf) = MagicBuffer::from_v8(scope, value) {
|
||||
return Ok(Self::Buffer(buf));
|
||||
} else if let Ok(s) = crate::from_v8(scope, value) {
|
||||
return Ok(Self::String(s));
|
||||
}
|
||||
Err(Error::ExpectedBuffer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ use crate::error::{Error, Result};
|
|||
use crate::keys::v8_struct_key;
|
||||
use crate::magic::transl8::MAGIC_FIELD;
|
||||
use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8};
|
||||
use crate::{magic, Buffer, ByteString, DetachedBuffer, U16String};
|
||||
use crate::{
|
||||
magic, Buffer, ByteString, DetachedBuffer, StringOrBuffer, U16String,
|
||||
};
|
||||
|
||||
type JsValue<'s> = v8::Local<'s, v8::Value>;
|
||||
type JsResult<'s> = Result<JsValue<'s>>;
|
||||
|
@ -264,6 +266,7 @@ pub enum StructSerializers<'a, 'b, 'c> {
|
|||
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
|
||||
MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>),
|
||||
MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>),
|
||||
MagicStringOrBuffer(MagicalSerializer<'a, 'b, 'c, StringOrBuffer>),
|
||||
Regular(ObjectSerializer<'a, 'b, 'c>),
|
||||
}
|
||||
|
||||
|
@ -282,6 +285,9 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
|
|||
StructSerializers::MagicDetached(s) => s.serialize_field(key, value),
|
||||
StructSerializers::MagicByteString(s) => s.serialize_field(key, value),
|
||||
StructSerializers::MagicU16String(s) => s.serialize_field(key, value),
|
||||
StructSerializers::MagicStringOrBuffer(s) => {
|
||||
s.serialize_field(key, value)
|
||||
}
|
||||
StructSerializers::Regular(s) => s.serialize_field(key, value),
|
||||
}
|
||||
}
|
||||
|
@ -293,6 +299,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> {
|
|||
StructSerializers::MagicDetached(s) => s.end(),
|
||||
StructSerializers::MagicByteString(s) => s.end(),
|
||||
StructSerializers::MagicU16String(s) => s.end(),
|
||||
StructSerializers::MagicStringOrBuffer(s) => s.end(),
|
||||
StructSerializers::Regular(s) => s.end(),
|
||||
}
|
||||
}
|
||||
|
@ -535,6 +542,10 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> {
|
|||
let m = MagicalSerializer::<DetachedBuffer>::new(self.scope);
|
||||
Ok(StructSerializers::MagicDetached(m))
|
||||
}
|
||||
StringOrBuffer::MAGIC_NAME => {
|
||||
let m = MagicalSerializer::<StringOrBuffer>::new(self.scope);
|
||||
Ok(StructSerializers::MagicStringOrBuffer(m))
|
||||
}
|
||||
magic::Value::MAGIC_NAME => {
|
||||
let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope);
|
||||
Ok(StructSerializers::Magic(m))
|
||||
|
|
|
@ -178,24 +178,24 @@ fn de_map() {
|
|||
fn de_string_or_buffer() {
|
||||
dedo("'hello'", |scope, v| {
|
||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(sob.as_slice(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]);
|
||||
assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]);
|
||||
});
|
||||
|
||||
dedo("new Uint8Array([97])", |scope, v| {
|
||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(sob.as_slice(), &[97]);
|
||||
assert_eq!(sob.as_ref(), &[97]);
|
||||
});
|
||||
|
||||
dedo("new Uint8Array([128])", |scope, v| {
|
||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(sob.as_slice(), &[128]);
|
||||
assert_eq!(sob.as_ref(), &[128]);
|
||||
});
|
||||
|
||||
dedo(
|
||||
"(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))",
|
||||
|scope, v| {
|
||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
||||
assert_eq!(sob.as_slice(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]);
|
||||
assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue