Unverified Commit 7c04d230 authored by thiolliere's avatar thiolliere Committed by GitHub
Browse files

Impl for u8 by specializing at compile-time (#83)

Encode/Decode has now a new associated const that tells if the type is u8, that allow some kind of specialization when implementing Encode/Decode for `Vec<u8>`
parent b980954c
This diff is collapsed.
......@@ -18,6 +18,14 @@ byte-slice-cast = { version = "0.3", optional = true }
[dev-dependencies]
serde_derive = { version = "1.0" }
parity-codec-derive = { path = "derive", version = "3.3", default-features = false }
criterion = "0.2"
[[bench]]
name = "benches"
harness = false
[lib]
bench = false
[features]
default = ["std"]
......
......@@ -12,80 +12,65 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![feature(test)]
extern crate test;
#[macro_use]
extern crate criterion;
#[macro_use]
extern crate parity_codec_derive;
use parity_codec::{Encode, Decode};
use parity_codec::*;
use parity_codec_derive::{Encode, Decode};
#[bench]
fn array_vec_write_u128(b: &mut test::Bencher) {
use criterion::{Criterion, black_box, Bencher};
use std::time::Duration;
fn array_vec_write_u128(b: &mut Bencher) {
b.iter(|| {
for b in 0..test::black_box(1_000_000) {
for b in 0..black_box(1_000_000) {
let a = 0xffff_ffff_ffff_ffff_ffff_u128;
Compact(a ^ b).using_encoded(|x| {
test::black_box(x).len()
black_box(x).len()
});
}
});
}
fn test_vec<F: Fn(&mut Vec<u8>, &[u8])>(b: &mut test::Bencher, f: F) {
let f = test::black_box(f);
let x = test::black_box([0xff; 10240]);
fn test_vec<F: Fn(&mut Vec<u8>, &[u8])>(b: &mut Bencher, f: F) {
let f = black_box(f);
let x = black_box([0xff; 10240]);
b.iter(|| {
for _b in 0..test::black_box(10_000) {
for _b in 0..black_box(10_000) {
let mut vec = Vec::<u8>::new();
f(&mut vec, &x);
}
});
}
#[bench]
fn vec_write_as_output(b: &mut test::Bencher) {
fn vec_write_as_output(b: &mut Bencher) {
test_vec(b, |vec, a| {
Output::write(vec, a);
});
}
#[bench]
fn vec_extend(b: &mut test::Bencher) {
fn vec_extend(b: &mut Bencher) {
test_vec(b, |vec, a| {
vec.extend(a);
});
}
#[bench]
fn vec_extend_from_slice(b: &mut test::Bencher) {
fn vec_extend_from_slice(b: &mut Bencher) {
test_vec(b, |vec, a| {
vec.extend_from_slice(a);
});
}
#[bench]
fn encoding_of_large_vec_u8(b: &mut test::Bencher) {
let mut v = vec![];
for i in 0..256 {
v.push(i);
}
for _ in 0..12 {
v.extend(v.clone());
}
b.iter(|| {
v.encode();
})
}
#[derive(Encode, Decode)]
enum Event {
ComplexEvent(Vec<u8>, u32, i32, u128, i8),
}
#[bench]
fn vec_append_with_decode_and_encode(b: &mut test::Bencher) {
fn vec_append_with_decode_and_encode(b: &mut Bencher) {
let data = b"PCX";
b.iter(|| {
......@@ -101,8 +86,7 @@ fn vec_append_with_decode_and_encode(b: &mut test::Bencher) {
})
}
#[bench]
fn vec_append_with_encode_append(b: &mut test::Bencher) {
fn vec_append_with_encode_append(b: &mut Bencher) {
let data = b"PCX";
b.iter(|| {
......@@ -118,4 +102,46 @@ fn vec_append_with_encode_append(b: &mut test::Bencher) {
).unwrap();
}
});
}
\ No newline at end of file
}
fn encode_decode_vec_u8(c: &mut Criterion) {
c.bench_function_over_inputs("vec_u8_encode - Vec<u8>", |b, &vec_size| {
let vec: Vec<u8> = (0..=255u8)
.cycle()
.take(vec_size)
.collect();
let vec = black_box(vec);
b.iter(|| vec.encode())
}, vec![1, 2, 5, 32, 1024]);
c.bench_function_over_inputs("vec_u8_decode - Vec<u8>", |b, &vec_size| {
let vec: Vec<u8> = (0..=255u8)
.cycle()
.take(vec_size)
.collect();
let vec = vec.encode();
let vec = black_box(vec);
b.iter(|| {
let _: Vec<u8> = Decode::decode(&mut &vec[..]).unwrap();
})
}, vec![1, 2, 5, 32, 1024]);
}
fn bench_fn(c: &mut Criterion) {
c.bench_function("vec_write_as_output", vec_write_as_output);
c.bench_function("vec_extend", vec_extend);
c.bench_function("vec_extend_from_slice", vec_extend_from_slice);
c.bench_function("vec_append_with_decode_and_encode", vec_append_with_decode_and_encode);
c.bench_function("vec_append_with_encode_append", vec_append_with_encode_append);
c.bench_function("array_vec_write_u128", array_vec_write_u128);
}
criterion_group!{
name = benches;
config = Criterion::default().warm_up_time(Duration::from_millis(500)).without_plots();
targets = encode_decode_vec_u8, bench_fn
}
criterion_main!(benches);
......@@ -207,11 +207,23 @@ impl<T: arrayvec::Array<Item=u8>> Output for ArrayVecWrapper<T> {
}
}
/// This enum must not be exported and must only be instantiable by parity-codec.
/// Because implementation of Encode and Decode for u8 is done in this crate
/// and there is not other usage.
pub enum IsU8 {
Yes,
No,
}
/// Trait that allows zero-copy write of value-references to slices in LE format.
///
/// Implementations should override `using_encoded` for value types and `encode_to` and `size_hint` for allocating types.
/// Wrapper types should override all methods.
pub trait Encode {
#[doc(hidden)]
// This const is used to optimise implementation of codec for Vec<u8>.
const IS_U8: IsU8 = IsU8::No;
/// If possible give a hint of expected size of the encoding.
///
/// This method is used inside default implementation of `encode`
......@@ -250,6 +262,9 @@ pub trait EncodeAppend {
/// Trait that allows zero-copy read of value-references from slices in LE format.
pub trait Decode: Sized {
#[doc(hidden)]
const IS_U8: IsU8 = IsU8::No;
/// Attempt to deserialise the value from input.
fn decode<I: Input>(value: &mut I) -> Result<Self, Error>;
}
......@@ -1020,30 +1035,6 @@ impl_array!(
253, 254, 255, 256, 384, 512, 768, 1024, 2048, 4096, 8192, 16384, 32768,
);
impl Encode for [u8] {
fn size_hint(&self) -> usize {
self.len() + mem::size_of::<u32>()
}
fn encode_to<W: Output>(&self, dest: &mut W) {
let len = self.len();
assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements.");
Compact(len as u32).encode_to(dest);
dest.write(self)
}
}
impl Decode for Vec<u8> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
<Compact<u32>>::decode(input).and_then(move |Compact(len)| {
let len = len as usize;
let mut vec = vec![0; len];
input.read(&mut vec[..len])?;
Ok(vec)
})
}
}
impl Encode for str {
fn size_hint(&self) -> usize {
self.as_bytes().size_hint()
......@@ -1089,12 +1080,27 @@ impl Decode for String {
}
impl<T: Encode> Encode for [T] {
fn size_hint(&self) -> usize {
if let IsU8::Yes = <T as Encode>::IS_U8 {
self.len() + mem::size_of::<u32>()
} else {
0
}
}
fn encode_to<W: Output>(&self, dest: &mut W) {
let len = self.len();
assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements.");
Compact(len as u32).encode_to(dest);
for item in self {
item.encode_to(dest);
if let IsU8::Yes= <T as Encode>::IS_U8 {
let self_transmute = unsafe {
std::mem::transmute::<&[T], &[u8]>(self)
};
dest.write(self_transmute)
} else {
for item in self {
item.encode_to(dest);
}
}
}
}
......@@ -1102,11 +1108,23 @@ impl<T: Encode> Encode for [T] {
impl<T: Decode> Decode for Vec<T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
<Compact<u32>>::decode(input).and_then(move |Compact(len)| {
let mut r = Vec::with_capacity(len as usize);
for _ in 0..len {
r.push(T::decode(input)?);
let len = len as usize;
if let IsU8::Yes = <T as Decode>::IS_U8 {
let mut r = vec![0; len];
if input.read(&mut r[..len])? != len {
Err(Error::from("Input vector len doesn't match prefix specified"))
} else {
let r = unsafe { std::mem::transmute::<Vec<u8>, Vec<T>>(r) };
Ok(r)
}
} else {
let mut r = Vec::with_capacity(len);
for _ in 0..len {
r.push(T::decode(input)?);
}
Ok(r)
}
Ok(r)
})
}
}
......@@ -1371,10 +1389,12 @@ macro_rules! impl_endians {
)* }
}
macro_rules! impl_non_endians {
( $( $t:ty ),* ) => { $(
( $( $t:ty $( { $is_u8:ident } )? ),* ) => { $(
impl EndianSensitive for $t {}
impl Encode for $t {
$( const $is_u8: IsU8 = IsU8::Yes; )?
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.as_le_then(|le| {
let size = mem::size_of::<$t>();
......@@ -1393,6 +1413,8 @@ macro_rules! impl_non_endians {
}
impl Decode for $t {
$( const $is_u8: IsU8 = IsU8::Yes; )?
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let size = mem::size_of::<$t>();
assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type.");
......@@ -1412,12 +1434,7 @@ macro_rules! impl_non_endians {
}
impl_endians!(u16, u32, u64, u128, i16, i32, i64, i128);
impl_non_endians!(
i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28],
[u8; 32], [u8; 40], [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96],
[u8; 112], [u8; 128], bool
);
impl_non_endians!(u8 {IS_U8}, i8, bool);
#[cfg(test)]
......
......@@ -465,3 +465,16 @@ fn encode_decode_empty_enum() {
assert_eq!(EmptyEnumDerive::decode(&mut &[1, 2, 3][..]), Err("No such variant in enum EmptyEnumDerive".into()));
}
#[test]
fn codec_vec_u8() {
for v in [
vec![0u8; 0],
vec![0u8; 10],
vec![0u8; 100],
vec![0u8; 1000],
].into_iter() {
let e = v.encode();
assert_eq!(v, &Vec::<u8>::decode(&mut &e[..]).unwrap());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment