mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 21:50:00 -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",
|
"bencher",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"v8",
|
"v8",
|
||||||
]
|
]
|
||||||
|
|
|
@ -38,6 +38,7 @@ use deno_websocket::ws_create_server_stream;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use fly_accept_encoding::Encoding;
|
use fly_accept_encoding::Encoding;
|
||||||
|
use hyper::body::Bytes;
|
||||||
use hyper::server::conn::Http;
|
use hyper::server::conn::Http;
|
||||||
use hyper::service::Service;
|
use hyper::service::Service;
|
||||||
use hyper::Body;
|
use hyper::Body;
|
||||||
|
@ -612,7 +613,7 @@ async fn op_http_write_headers(
|
||||||
// (~4MB)
|
// (~4MB)
|
||||||
let mut writer =
|
let mut writer =
|
||||||
brotli::CompressorWriter::new(Vec::new(), 4096, 6, 22);
|
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())?;
|
body = builder.body(writer.into_inner().into())?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -623,14 +624,14 @@ async fn op_http_write_headers(
|
||||||
// 1.
|
// 1.
|
||||||
// https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_comp_level
|
// https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_comp_level
|
||||||
let mut writer = GzEncoder::new(Vec::new(), Compression::new(1));
|
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())?;
|
body = builder.body(writer.finish()?.into())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If a buffer was passed, but isn't compressible, we use it to
|
// If a buffer was passed, but isn't compressible, we use it to
|
||||||
// construct a response body.
|
// construct a response body.
|
||||||
body = builder.body(data.into_bytes().into())?;
|
body = builder.body(Bytes::copy_from_slice(&data).into())?;
|
||||||
}
|
}
|
||||||
new_wr = HttpResponseWriter::Closed;
|
new_wr = HttpResponseWriter::Closed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ path = "lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
serde = { version = "1.0.130", features = ["derive"] }
|
serde = { version = "1.0.130", features = ["derive"] }
|
||||||
serde_bytes = "0.11"
|
|
||||||
v8 = "0.42.0"
|
v8 = "0.42.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[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!(
|
benchmark_group!(
|
||||||
benches,
|
benches,
|
||||||
de_struct_v8,
|
de_struct_v8,
|
||||||
|
@ -174,6 +222,12 @@ benchmark_group!(
|
||||||
de_tuple_v8_opt,
|
de_tuple_v8_opt,
|
||||||
de_bstr_v8_12_b,
|
de_bstr_v8_12_b,
|
||||||
de_bstr_v8_1024_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);
|
benchmark_main!(benches);
|
||||||
|
|
|
@ -7,7 +7,9 @@ use crate::keys::{v8_struct_key, KeyCache};
|
||||||
use crate::magic::transl8::FromV8;
|
use crate::magic::transl8::FromV8;
|
||||||
use crate::magic::transl8::{visit_magic, MagicType};
|
use crate::magic::transl8::{visit_magic, MagicType};
|
||||||
use crate::payload::ValueType;
|
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> {
|
pub struct Deserializer<'a, 'b, 's> {
|
||||||
input: v8::Local<'a, v8::Value>,
|
input: v8::Local<'a, v8::Value>,
|
||||||
|
@ -359,6 +361,9 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
||||||
U16String::MAGIC_NAME => {
|
U16String::MAGIC_NAME => {
|
||||||
visit_magic(visitor, U16String::from_v8(self.scope, self.input)?)
|
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 => {
|
magic::Value::MAGIC_NAME => {
|
||||||
visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?)
|
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.
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -21,6 +22,12 @@ pub enum MagicBuffer {
|
||||||
|
|
||||||
impl_magic!(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 {
|
impl MagicBuffer {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
MagicBuffer::ToV8(Mutex::new(Some(vec![0_u8; 0].into_boxed_slice())))
|
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;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StringOrBuffer(Vec<u8>);
|
pub enum StringOrBuffer {
|
||||||
|
Buffer(MagicBuffer),
|
||||||
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>),
|
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringOrBufferInner {
|
impl_magic!(StringOrBuffer);
|
||||||
fn into_bytes(self) -> Vec<u8> {
|
|
||||||
|
impl Deref for StringOrBuffer {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
match self {
|
match self {
|
||||||
Self::String(s) => s.into_bytes(),
|
Self::Buffer(b) => b.as_ref(),
|
||||||
Self::Buffer(b) => b,
|
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::keys::v8_struct_key;
|
||||||
use crate::magic::transl8::MAGIC_FIELD;
|
use crate::magic::transl8::MAGIC_FIELD;
|
||||||
use crate::magic::transl8::{opaque_deref, opaque_recv, MagicType, ToV8};
|
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 JsValue<'s> = v8::Local<'s, v8::Value>;
|
||||||
type JsResult<'s> = Result<JsValue<'s>>;
|
type JsResult<'s> = Result<JsValue<'s>>;
|
||||||
|
@ -264,6 +266,7 @@ pub enum StructSerializers<'a, 'b, 'c> {
|
||||||
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
|
MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>),
|
||||||
MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>),
|
MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>),
|
||||||
MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>),
|
MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>),
|
||||||
|
MagicStringOrBuffer(MagicalSerializer<'a, 'b, 'c, StringOrBuffer>),
|
||||||
Regular(ObjectSerializer<'a, 'b, 'c>),
|
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::MagicDetached(s) => s.serialize_field(key, value),
|
||||||
StructSerializers::MagicByteString(s) => s.serialize_field(key, value),
|
StructSerializers::MagicByteString(s) => s.serialize_field(key, value),
|
||||||
StructSerializers::MagicU16String(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),
|
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::MagicDetached(s) => s.end(),
|
||||||
StructSerializers::MagicByteString(s) => s.end(),
|
StructSerializers::MagicByteString(s) => s.end(),
|
||||||
StructSerializers::MagicU16String(s) => s.end(),
|
StructSerializers::MagicU16String(s) => s.end(),
|
||||||
|
StructSerializers::MagicStringOrBuffer(s) => s.end(),
|
||||||
StructSerializers::Regular(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);
|
let m = MagicalSerializer::<DetachedBuffer>::new(self.scope);
|
||||||
Ok(StructSerializers::MagicDetached(m))
|
Ok(StructSerializers::MagicDetached(m))
|
||||||
}
|
}
|
||||||
|
StringOrBuffer::MAGIC_NAME => {
|
||||||
|
let m = MagicalSerializer::<StringOrBuffer>::new(self.scope);
|
||||||
|
Ok(StructSerializers::MagicStringOrBuffer(m))
|
||||||
|
}
|
||||||
magic::Value::MAGIC_NAME => {
|
magic::Value::MAGIC_NAME => {
|
||||||
let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope);
|
let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope);
|
||||||
Ok(StructSerializers::Magic(m))
|
Ok(StructSerializers::Magic(m))
|
||||||
|
|
|
@ -178,24 +178,24 @@ fn de_map() {
|
||||||
fn de_string_or_buffer() {
|
fn de_string_or_buffer() {
|
||||||
dedo("'hello'", |scope, v| {
|
dedo("'hello'", |scope, v| {
|
||||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
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| {
|
dedo("new Uint8Array([97])", |scope, v| {
|
||||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
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| {
|
dedo("new Uint8Array([128])", |scope, v| {
|
||||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
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(
|
dedo(
|
||||||
"(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))",
|
"(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))",
|
||||||
|scope, v| {
|
|scope, v| {
|
||||||
let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap();
|
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…
Add table
Reference in a new issue