1
0
Fork 0
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:
Aaron O'Mullan 2022-04-24 09:28:46 -03:00 committed by GitHub
parent 2eb8c3b82f
commit 4b7d306a19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 127 additions and 46 deletions

1
Cargo.lock generated
View file

@ -3568,7 +3568,6 @@ dependencies = [
"bencher",
"derive_more",
"serde",
"serde_bytes",
"serde_json",
"v8",
]

View file

@ -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;
}

View file

@ -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]

View file

@ -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);

View file

@ -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)?)
}

View file

@ -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())))

View file

@ -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)
}
}

View file

@ -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))

View file

@ -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]);
},
);
}