From 480c5ff160289204d36aa68756c0873dc54f09fa Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 6 May 2019 09:26:58 +1000 Subject: [PATCH] Update SszEncoder --- eth2/utils/ssz/benches/benches.rs | 42 +++++++++++++++++++ .../ssz/examples/large_list_of_structs.rs | 32 ++++++++++++++ eth2/utils/ssz/examples/struct_definition.rs | 4 +- eth2/utils/ssz/src/encode.rs | 15 ++++--- eth2/utils/ssz/src/encode/impls.rs | 22 ++-------- eth2/utils/ssz/src/lib.rs | 10 ++--- eth2/utils/ssz_derive/src/lib.rs | 16 ++++--- 7 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 eth2/utils/ssz/examples/large_list_of_structs.rs diff --git a/eth2/utils/ssz/benches/benches.rs b/eth2/utils/ssz/benches/benches.rs index 45a57adef..f774f1ce5 100644 --- a/eth2/utils/ssz/benches/benches.rs +++ b/eth2/utils/ssz/benches/benches.rs @@ -4,6 +4,15 @@ extern crate criterion; use criterion::black_box; use criterion::{Benchmark, Criterion}; use ssz::{Decodable, Encodable}; +use ssz_derive::{Decode, Encode}; + +#[derive(Clone, Copy, Encode, Decode)] +pub struct FixedLen { + a: u64, + b: u64, + c: u64, + d: u64, +} fn criterion_benchmark(c: &mut Criterion) { let n = 8196; @@ -32,6 +41,39 @@ fn criterion_benchmark(c: &mut Criterion) { }) .sample_size(100), ); + + let fixed_len = FixedLen { + a: 42, + b: 42, + c: 42, + d: 42, + }; + let fixed_len_vec: Vec = vec![fixed_len; 8196]; + + let vec = fixed_len_vec.clone(); + c.bench( + &format!("vec_of_{}_struct", n), + Benchmark::new("as_ssz_bytes", move |b| { + b.iter_with_setup(|| vec.clone(), |vec| black_box(vec.as_ssz_bytes())) + }) + .sample_size(100), + ); + + let vec = fixed_len_vec.clone(); + let bytes = vec.as_ssz_bytes(); + c.bench( + &format!("vec_of_{}_struct", n), + Benchmark::new("from_ssz_bytes", move |b| { + b.iter_with_setup( + || bytes.clone(), + |bytes| { + let vec: Vec = Vec::from_ssz_bytes(&bytes).unwrap(); + black_box(vec) + }, + ) + }) + .sample_size(100), + ); } criterion_group!(benches, criterion_benchmark); diff --git a/eth2/utils/ssz/examples/large_list_of_structs.rs b/eth2/utils/ssz/examples/large_list_of_structs.rs new file mode 100644 index 000000000..52e4f0e55 --- /dev/null +++ b/eth2/utils/ssz/examples/large_list_of_structs.rs @@ -0,0 +1,32 @@ +//! Encode and decode a list many times. +//! +//! Useful for `cargo flamegraph`. + +use ssz::{Decodable, Encodable}; +use ssz_derive::{Decode, Encode}; + +#[derive(Clone, Copy, Encode, Decode)] +pub struct FixedLen { + a: u64, + b: u64, + c: u64, + d: u64, +} + +fn main() { + let fixed_len = FixedLen { + a: 42, + b: 42, + c: 42, + d: 42, + }; + + let vec: Vec = vec![fixed_len; 8196]; + + let output: Vec> = (0..40_000) + .into_iter() + .map(|_| Vec::from_ssz_bytes(&vec.as_ssz_bytes()).unwrap()) + .collect(); + + println!("{}", output.len()); +} diff --git a/eth2/utils/ssz/examples/struct_definition.rs b/eth2/utils/ssz/examples/struct_definition.rs index eecf0ff14..04741db64 100644 --- a/eth2/utils/ssz/examples/struct_definition.rs +++ b/eth2/utils/ssz/examples/struct_definition.rs @@ -1,4 +1,4 @@ -use ssz::{Decodable, DecodeError, Encodable, SszDecoderBuilder, SszEncoder, SszStream}; +use ssz::{Decodable, DecodeError, Encodable, SszDecoderBuilder, SszEncoder}; #[derive(Debug, PartialEq)] pub struct Foo { @@ -23,7 +23,7 @@ impl Encodable for Foo { encoder.append(&self.b); encoder.append(&self.c); - buf.append(&mut encoder.drain()); + encoder.drain_onto(buf); } } diff --git a/eth2/utils/ssz/src/encode.rs b/eth2/utils/ssz/src/encode.rs index 072d82262..81b4a1027 100644 --- a/eth2/utils/ssz/src/encode.rs +++ b/eth2/utils/ssz/src/encode.rs @@ -32,10 +32,14 @@ pub struct SszEncoder { } impl SszEncoder { + pub fn list(num_fixed_bytes: usize) -> Self { + Self::container(num_fixed_bytes) + } + pub fn container(num_fixed_bytes: usize) -> Self { Self { offset: num_fixed_bytes, - fixed_bytes: vec![], + fixed_bytes: Vec::with_capacity(num_fixed_bytes), variable_bytes: vec![], } } @@ -51,10 +55,9 @@ impl SszEncoder { } } - pub fn drain(mut self) -> Vec { - self.fixed_bytes.append(&mut self.variable_bytes); - - self.fixed_bytes + pub fn drain_onto(mut self, buf: &mut Vec) { + buf.append(&mut self.fixed_bytes); + buf.append(&mut self.variable_bytes); } } @@ -63,6 +66,7 @@ pub struct VariableLengths { pub variable_bytes_length: usize, } +/* /// Provides a buffer for appending SSZ values. #[derive(Default)] pub struct SszStream { @@ -152,6 +156,7 @@ impl SszStream { self.fixed_bytes } } +*/ /// Encode `len` as a little-endian byte vec of `BYTES_PER_LENGTH_OFFSET` length. /// diff --git a/eth2/utils/ssz/src/encode/impls.rs b/eth2/utils/ssz/src/encode/impls.rs index 017b0f29f..1403e2c2e 100644 --- a/eth2/utils/ssz/src/encode/impls.rs +++ b/eth2/utils/ssz/src/encode/impls.rs @@ -31,7 +31,6 @@ impl Encodable for Vec { } fn ssz_append(&self, buf: &mut Vec) { - if T::is_ssz_fixed_len() { buf.reserve(T::ssz_fixed_len() * self.len()); @@ -39,28 +38,13 @@ impl Encodable for Vec { item.ssz_append(buf); } } else { - /* - for item in self { - let mut substream = SszStream::new(); - - item.ssz_append(&mut substream); - - s.append_variable_bytes(&substream.drain()); - } - */ - let mut offset = self.len() * BYTES_PER_LENGTH_OFFSET; - let mut fixed = Vec::with_capacity(offset); - let mut variable = vec![]; + let mut encoder = SszEncoder::list(self.len() * BYTES_PER_LENGTH_OFFSET); for item in self { - fixed.append(&mut encode_length(offset)); - let mut bytes = item.as_ssz_bytes(); - offset += bytes.len(); - variable.append(&mut bytes); + encoder.append(item); } - buf.append(&mut fixed); - buf.append(&mut variable); + encoder.drain_onto(buf); } } } diff --git a/eth2/utils/ssz/src/lib.rs b/eth2/utils/ssz/src/lib.rs index c91acb70b..2c206dbf1 100644 --- a/eth2/utils/ssz/src/lib.rs +++ b/eth2/utils/ssz/src/lib.rs @@ -17,21 +17,19 @@ mod decode; mod encode; pub use decode::{Decodable, DecodeError, SszDecoderBuilder}; -pub use encode::{Encodable, SszEncoder, SszStream}; +pub use encode::{Encodable, SszEncoder}; pub const BYTES_PER_LENGTH_OFFSET: usize = 4; pub const MAX_LENGTH_VALUE: usize = 1 << (BYTES_PER_LENGTH_OFFSET * 8) - 1; /// Convenience function to SSZ encode an object supporting ssz::Encode. +/// +/// Equivalent to `val.as_ssz_bytes()`. pub fn ssz_encode(val: &T) -> Vec where T: Encodable, { - let mut buf = vec![]; - - val.ssz_append(&mut buf); - - buf + val.as_ssz_bytes() } /* diff --git a/eth2/utils/ssz_derive/src/lib.rs b/eth2/utils/ssz_derive/src/lib.rs index 56820256c..185ede2d9 100644 --- a/eth2/utils/ssz_derive/src/lib.rs +++ b/eth2/utils/ssz_derive/src/lib.rs @@ -1,4 +1,4 @@ -#![recursion_limit="128"] +#![recursion_limit = "128"] extern crate proc_macro; @@ -81,6 +81,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream { let field_idents = get_serializable_named_field_idents(&struct_data); let field_types_a = get_serializable_field_types(&struct_data); let field_types_b = field_types_a.clone(); + let field_types_c = field_types_a.clone(); let output = quote! { impl ssz::Encodable for #name { @@ -102,14 +103,19 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream { } } - fn as_ssz_bytes(&self) -> Vec { - let mut stream = ssz::SszStream::new(); + fn ssz_append(&self, buf: &mut Vec) { + let offset = #( + <#field_types_c as ssz::Encodable>::ssz_fixed_len() + + )* + 0; + + let mut encoder = ssz::SszEncoder::container(offset); #( - stream.append(&self.#field_idents); + encoder.append(&self.#field_idents); )* - stream.drain() + encoder.drain_onto(buf); } } };