diff --git a/Cargo.lock b/Cargo.lock index bf9cf873e..7168257f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3402,6 +3402,7 @@ dependencies = [ name = "patricia-trie-ethereum" version = "0.1.0" dependencies = [ + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/util/patricia-trie-ethereum/Cargo.toml b/util/patricia-trie-ethereum/Cargo.toml index b8b902f33..cc7e8ce8f 100644 --- a/util/patricia-trie-ethereum/Cargo.toml +++ b/util/patricia-trie-ethereum/Cargo.toml @@ -18,3 +18,8 @@ elastic-array = "0.10" memory-db = "0.15.0" keccak-hash = "0.4.0" journaldb = { path = "../journaldb" } +criterion = "0.3" + +[[bench]] +name = "rlp_node_codec" +harness = false diff --git a/util/patricia-trie-ethereum/benches/rlp_node_codec.rs b/util/patricia-trie-ethereum/benches/rlp_node_codec.rs new file mode 100644 index 000000000..d16dd3c4d --- /dev/null +++ b/util/patricia-trie-ethereum/benches/rlp_node_codec.rs @@ -0,0 +1,83 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Benchmarking RlpNodeCodec decoding performance + +extern crate criterion; +extern crate patricia_trie_ethereum as ethtrie; +extern crate trie_db; +extern crate ethereum_types; +extern crate rlp; + +use criterion::{Criterion, criterion_group, criterion_main}; +use ethereum_types::H256; +use ethtrie::RlpNodeCodec; +use rlp::RlpStream; +use trie_db::NodeCodec; + +fn decoding(c: &mut Criterion) { + c.bench_function("decode leaf (inline)", |b| { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode extension (inline)", |b| { + let mut stream = RlpStream::new_list(2); + let payload = vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9u8]; + stream.append(&"").append(&payload); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode extension (hash)", |b| { + let mut stream = RlpStream::new_list(2); + let payload = H256::random(); + stream.append(&"").append(&payload); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode branch (hash)", |b| { + let mut stream = RlpStream::new_list(17); + for _ in 0..17 { + stream.append(&H256::random()); + + } + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode branch (inline)", |b| { + let mut stream = RlpStream::new_list(17); + for _ in 0..17 { + stream.append(&[&H256::random().as_bytes(), H256::random().as_bytes()].concat()); + } + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode empty data", |b| { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data)}); + }); +} + +criterion_group!(benches, decoding); +criterion_main!(benches); diff --git a/util/patricia-trie-ethereum/src/lib.rs b/util/patricia-trie-ethereum/src/lib.rs index d7b6a261c..0ed733283 100644 --- a/util/patricia-trie-ethereum/src/lib.rs +++ b/util/patricia-trie-ethereum/src/lib.rs @@ -148,11 +148,11 @@ pub type Result = trie::Result; #[cfg(test)] mod tests { - use ethereum_types::H256; - use crate::{TrieDB, TrieDBMut, trie::TrieMut}; use trie::Trie; + use crate::{TrieDB, TrieDBMut, trie::TrieMut}; + #[test] fn test_inline_encoding_branch() { let mut memdb = journaldb::new_memory_db(); diff --git a/util/patricia-trie-ethereum/src/rlp_node_codec.rs b/util/patricia-trie-ethereum/src/rlp_node_codec.rs index d44534365..73a65d5f3 100644 --- a/util/patricia-trie-ethereum/src/rlp_node_codec.rs +++ b/util/patricia-trie-ethereum/src/rlp_node_codec.rs @@ -24,11 +24,10 @@ use std::marker::PhantomData; use std::borrow::Borrow; use std::ops::Range; use trie::{ - NodeCodec, ChildReference, Partial, node::{NibbleSlicePlan, NodePlan, NodeHandlePlan}, + NodeCodec, ChildReference, Partial, + node::{NibbleSlicePlan, NodePlan, NodeHandlePlan}, }; - - /// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher` #[derive(Default, Clone)] pub struct RlpNodeCodec {mark: PhantomData} @@ -98,7 +97,7 @@ impl NodeCodec for RlpNodeCodec { HASHED_NULL_NODE } - fn decode_plan(data: &[u8]) -> ::std::result::Result { + fn decode_plan(data: &[u8]) -> Result { let r = Rlp::new(data); match r.prototype()? { // either leaf or extension - decode first item with NibbleSlice::??? @@ -230,3 +229,51 @@ impl NodeCodec for RlpNodeCodec { } } + +#[cfg(test)] +mod tests { + use trie::{NodeCodec, node::{Node, NodeHandle}, NibbleSlice}; + use rlp::RlpStream; + use RlpNodeCodec; + + #[test] + fn decode_leaf() { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let data = stream.out(); + let r = RlpNodeCodec::decode(&data); + assert!(r.is_ok()); + // "c" & 16 != 16 => `start` == 1 + let cat_nib = NibbleSlice::new(&b"at"[..]); + assert_eq!(r.unwrap(), Node::Leaf(cat_nib, &b"dog"[..])); + } + + #[test] + fn decode_ext() { + let mut stream = RlpStream::new_list(2); + let payload = vec![0x1, 0x2, 0x3u8]; + stream.append(&"").append(&payload); + let data = stream.out(); + let decoded = RlpNodeCodec::decode(&data); + + assert!(decoded.is_ok()); + assert_eq!( + decoded.unwrap(), + Node::Extension( + NibbleSlice::new(&[]), + NodeHandle::Inline(&[0x80 + 0x3, 0x1, 0x2, 0x3]) + ) + ); + } + + #[test] + fn decode_empty_data() { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + let data = stream.out(); + assert_eq!( + RlpNodeCodec::decode(&data), + Ok(Node::Empty), + ); + } +}