mirror of https://git.sr.ht/~stygianentity/bincode
Improved encoding and decoding speed of Vec<u8> (#619)
* Improved encoding and decoding speed of Vec<u8> * Added black_box calls to benches/string.rs Added a SizeWriter because someone finally has a benchmark to show it's faster * Improved performance for `impl<T> Encode for [T]` * Added #[inline] to `impl Encoder for EncoderImpl` --------- Co-authored-by: Victor Koenders <victor.koenders@qrtech.se>
This commit is contained in:
parent
3aa269bfea
commit
9880abe499
|
|
@ -44,6 +44,8 @@ rand = "0.8"
|
||||||
uuid = { version = "1.1", features = ["serde"] }
|
uuid = { version = "1.1", features = ["serde"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
glam = { version = "0.21", features = ["serde"] }
|
glam = { version = "0.21", features = ["serde"] }
|
||||||
|
bincode_1 = { version = "1.3", package = "bincode" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "varint"
|
name = "varint"
|
||||||
|
|
@ -53,9 +55,14 @@ harness = false
|
||||||
name = "inline"
|
name = "inline"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "string"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
debug = 1
|
debug = 1
|
||||||
|
lto = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
// https://github.com/bincode-org/bincode/issues/618
|
||||||
|
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Encode, Decode)]
|
||||||
|
pub struct MyStruct {
|
||||||
|
pub v: Vec<String>,
|
||||||
|
pub string: String,
|
||||||
|
pub number: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyStruct {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(v: Vec<String>, string: String, number: usize) -> Self {
|
||||||
|
Self { v, string, number }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_data(size: usize) -> Vec<MyStruct> {
|
||||||
|
(0..size)
|
||||||
|
.map(|i| {
|
||||||
|
let vec: Vec<String> = (0..i).map(|i| i.to_string().repeat(100)).collect();
|
||||||
|
MyStruct::new(vec, size.to_string(), size)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index_item_decode(c: &mut Criterion) {
|
||||||
|
let data = build_data(100);
|
||||||
|
|
||||||
|
c.bench_function("bench v1", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = black_box(bincode_1::serialize(black_box(&data))).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let config = bincode::config::standard();
|
||||||
|
c.bench_function("bench v2 (standard)", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = black_box(bincode::encode_to_vec(black_box(&data), config)).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let config = bincode::config::legacy();
|
||||||
|
c.bench_function("bench v2 (legacy)", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = black_box(bincode::encode_to_vec(black_box(&data), config)).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let encodedv1 = bincode_1::serialize(&data).unwrap();
|
||||||
|
let encodedv2 = bincode::encode_to_vec(&data, config).unwrap();
|
||||||
|
assert_eq!(encodedv1, encodedv2);
|
||||||
|
|
||||||
|
c.bench_function("bench v1 decode", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _: Vec<MyStruct> =
|
||||||
|
black_box(bincode_1::deserialize(black_box(&encodedv1))).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("bench v2 decode (legacy)", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _: (Vec<MyStruct>, _) =
|
||||||
|
black_box(bincode::decode_from_slice(black_box(&encodedv1), config)).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, index_item_decode);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
@ -32,6 +32,7 @@ impl<W: Writer, C: Config> EncoderImpl<W, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the underlying writer
|
/// Return the underlying writer
|
||||||
|
#[inline]
|
||||||
pub fn into_writer(self) -> W {
|
pub fn into_writer(self) -> W {
|
||||||
self.writer
|
self.writer
|
||||||
}
|
}
|
||||||
|
|
@ -42,10 +43,12 @@ impl<W: Writer, C: Config> Encoder for EncoderImpl<W, C> {
|
||||||
|
|
||||||
type C = C;
|
type C = C;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn writer(&mut self) -> &mut Self::W {
|
fn writer(&mut self) -> &mut Self::W {
|
||||||
&mut self.writer
|
&mut self.writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn config(&self) -> &Self::C {
|
fn config(&self) -> &Self::C {
|
||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,10 +295,17 @@ impl Encode for char {
|
||||||
|
|
||||||
impl<T> Encode for [T]
|
impl<T> Encode for [T]
|
||||||
where
|
where
|
||||||
T: Encode,
|
T: Encode + 'static,
|
||||||
{
|
{
|
||||||
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
|
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
|
||||||
super::encode_slice_len(encoder, self.len())?;
|
super::encode_slice_len(encoder, self.len())?;
|
||||||
|
|
||||||
|
if core::any::TypeId::of::<T>() == core::any::TypeId::of::<u8>() {
|
||||||
|
let t: &[u8] = unsafe { core::mem::transmute(self) };
|
||||||
|
encoder.writer().write(t)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
for item in self {
|
for item in self {
|
||||||
item.encode(encoder)?;
|
item.encode(encoder)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,3 +65,18 @@ impl<'storage> Writer for SliceWriter<'storage> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A writer that counts how many bytes were written. This is useful for e.g. pre-allocating buffers bfeore writing to them.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SizeWriter {
|
||||||
|
/// the amount of bytes that were written so far
|
||||||
|
pub bytes_written: usize,
|
||||||
|
}
|
||||||
|
impl Writer for SizeWriter {
|
||||||
|
#[inline(always)]
|
||||||
|
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
|
||||||
|
self.bytes_written += bytes.len();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
de::{BorrowDecoder, Decode, Decoder},
|
de::{read::Reader, BorrowDecoder, Decode, Decoder},
|
||||||
enc::{self, Encode, Encoder},
|
enc::{
|
||||||
|
self,
|
||||||
|
write::{SizeWriter, Writer},
|
||||||
|
Encode, Encoder,
|
||||||
|
},
|
||||||
error::{DecodeError, EncodeError},
|
error::{DecodeError, EncodeError},
|
||||||
impl_borrow_decode, BorrowDecode, Config,
|
impl_borrow_decode, BorrowDecode, Config,
|
||||||
};
|
};
|
||||||
|
|
@ -21,6 +25,12 @@ pub(crate) struct VecWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VecWriter {
|
impl VecWriter {
|
||||||
|
/// Create a new vec writer with the given capacity
|
||||||
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Vec::with_capacity(cap),
|
||||||
|
}
|
||||||
|
}
|
||||||
// May not be used in all feature combinations
|
// May not be used in all feature combinations
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn collect(self) -> Vec<u8> {
|
pub(crate) fn collect(self) -> Vec<u8> {
|
||||||
|
|
@ -29,6 +39,7 @@ impl VecWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl enc::write::Writer for VecWriter {
|
impl enc::write::Writer for VecWriter {
|
||||||
|
#[inline(always)]
|
||||||
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
|
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
|
||||||
self.inner.extend_from_slice(bytes);
|
self.inner.extend_from_slice(bytes);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -40,7 +51,12 @@ impl enc::write::Writer for VecWriter {
|
||||||
/// [config]: config/index.html
|
/// [config]: config/index.html
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
|
||||||
pub fn encode_to_vec<E: enc::Encode, C: Config>(val: E, config: C) -> Result<Vec<u8>, EncodeError> {
|
pub fn encode_to_vec<E: enc::Encode, C: Config>(val: E, config: C) -> Result<Vec<u8>, EncodeError> {
|
||||||
let writer = VecWriter::default();
|
let size = {
|
||||||
|
let mut size_writer = enc::EncoderImpl::<_, C>::new(SizeWriter::default(), config);
|
||||||
|
val.encode(&mut size_writer)?;
|
||||||
|
size_writer.into_writer().bytes_written
|
||||||
|
};
|
||||||
|
let writer = VecWriter::with_capacity(size);
|
||||||
let mut encoder = enc::EncoderImpl::<_, C>::new(writer, config);
|
let mut encoder = enc::EncoderImpl::<_, C>::new(writer, config);
|
||||||
val.encode(&mut encoder)?;
|
val.encode(&mut encoder)?;
|
||||||
Ok(encoder.into_writer().inner)
|
Ok(encoder.into_writer().inner)
|
||||||
|
|
@ -262,10 +278,20 @@ where
|
||||||
|
|
||||||
impl<T> Decode for Vec<T>
|
impl<T> Decode for Vec<T>
|
||||||
where
|
where
|
||||||
T: Decode,
|
T: Decode + 'static,
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(decoder)?;
|
let len = crate::de::decode_slice_len(decoder)?;
|
||||||
|
|
||||||
|
if core::any::TypeId::of::<T>() == core::any::TypeId::of::<u8>() {
|
||||||
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
// optimize for reading u8 vecs
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
vec.resize(len, 0u8);
|
||||||
|
decoder.reader().read(&mut vec)?;
|
||||||
|
// Safety: Vec<T> is Vec<u8>
|
||||||
|
return Ok(unsafe { core::mem::transmute(vec) });
|
||||||
|
}
|
||||||
decoder.claim_container_read::<T>(len)?;
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
|
||||||
let mut vec = Vec::with_capacity(len);
|
let mut vec = Vec::with_capacity(len);
|
||||||
|
|
@ -300,10 +326,15 @@ where
|
||||||
|
|
||||||
impl<T> Encode for Vec<T>
|
impl<T> Encode for Vec<T>
|
||||||
where
|
where
|
||||||
T: Encode,
|
T: Encode + 'static,
|
||||||
{
|
{
|
||||||
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
|
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
|
||||||
crate::enc::encode_slice_len(encoder, self.len())?;
|
crate::enc::encode_slice_len(encoder, self.len())?;
|
||||||
|
if core::any::TypeId::of::<T>() == core::any::TypeId::of::<u8>() {
|
||||||
|
let slice: &[u8] = unsafe { core::mem::transmute(self.as_slice()) };
|
||||||
|
encoder.writer().write(slice)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
for item in self.iter() {
|
for item in self.iter() {
|
||||||
item.encode(encoder)?;
|
item.encode(encoder)?;
|
||||||
}
|
}
|
||||||
|
|
@ -364,7 +395,7 @@ where
|
||||||
|
|
||||||
impl<T> Decode for Box<[T]>
|
impl<T> Decode for Box<[T]>
|
||||||
where
|
where
|
||||||
T: Decode,
|
T: Decode + 'static,
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
||||||
let vec = Vec::decode(decoder)?;
|
let vec = Vec::decode(decoder)?;
|
||||||
|
|
@ -444,7 +475,7 @@ where
|
||||||
|
|
||||||
impl<T> Decode for Rc<[T]>
|
impl<T> Decode for Rc<[T]>
|
||||||
where
|
where
|
||||||
T: Decode,
|
T: Decode + 'static,
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
||||||
let vec = Vec::decode(decoder)?;
|
let vec = Vec::decode(decoder)?;
|
||||||
|
|
@ -513,7 +544,7 @@ where
|
||||||
#[cfg(target_has_atomic = "ptr")]
|
#[cfg(target_has_atomic = "ptr")]
|
||||||
impl<T> Decode for Arc<[T]>
|
impl<T> Decode for Arc<[T]>
|
||||||
where
|
where
|
||||||
T: Decode,
|
T: Decode + 'static,
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
|
||||||
let vec = Vec::decode(decoder)?;
|
let vec = Vec::decode(decoder)?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue