feat(db): fuzzing & benchmark (#86)

* fuzz with test-fuzz

* move fuzzing to db/codecs

* add criterion & iai

* print encoded size sum on criterion benchmark

* fix BlockNumHash encode

* add gh action for benchmarks

* don't sum results

* test ci values

* Revert "test ci values"

This reverts commit cc47fd80538b2c0073592f824c2693c927021f8f.

* specify criterion version

* add docs

* remove benchmark job
This commit is contained in:
joshieDo
2022-10-20 04:08:07 +08:00
committed by GitHub
parent 7e26ba5090
commit 630baf5d70
14 changed files with 396 additions and 23 deletions

1
crates/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

View File

@ -42,7 +42,7 @@ pub fn use_scale(_args: TokenStream, input: TokenStream) -> TokenStream {
}
quote! {
#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode)]
#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode, serde::Serialize, serde::Deserialize)]
#ast
}
.into()

View File

@ -13,8 +13,7 @@ reth-primitives = { path = "../primitives" }
# codecs
serde = { version = "1.0.*", default-features = false }
postcard = { version = "1.0.2", features = ["heapless"] }
heapless = "0.7.16"
postcard = { version = "1.0.2", features = ["alloc"] }
parity-scale-codec = { version = "3.2.1", features = ["bytes"] }
# misc
@ -26,6 +25,19 @@ tempfile = { version = "3.3.0", optional = true }
[dev-dependencies]
tempfile = "3.3.0"
test-fuzz = "3.0.4"
criterion = "0.4.0"
iai = "0.1.1"
[features]
test-utils = ["tempfile"]
bench-postcard = ["bench"]
bench = []
[[bench]]
name = "encoding_crit"
harness = false
[[bench]]
name = "encoding_iai"
harness = false

View File

@ -0,0 +1,15 @@
# DB Benchmarks
## Codecs
Currently only benchmarking the encoding/decoding of `Header`. It can be benchmarked with two different codecs at the moment `main/scale` and `postcard`:
### Main/Scale:
```bash
$ cargo bench --features bench
```
### Postcard:
```bash
$ cargo bench --features bench-postcard
```

View File

@ -0,0 +1,32 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
/// Benchmarks the encoding and decoding of `Header` using criterion.
macro_rules! impl_criterion_encoding_benchmark {
($name:tt) => {
pub fn criterion_benchmark(c: &mut Criterion) {
let mut size = 0;
c.bench_function(stringify!($name), |b| {
b.iter(|| {
let encoded_size = reth_db::kv::codecs::fuzz::Header::encode_and_decode(
black_box(reth_primitives::Header::default()),
)
.0;
if size == 0 {
size = encoded_size;
}
})
});
println!("Size (bytes): `{size}`");
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
};
}
#[cfg(not(feature = "bench-postcard"))]
impl_criterion_encoding_benchmark!(scale);
#[cfg(feature = "bench-postcard")]
impl_criterion_encoding_benchmark!(postcard);

View File

@ -0,0 +1,20 @@
use iai::{black_box, main};
/// Benchmarks the encoding and decoding of `Header` using iai.
macro_rules! impl_iai_encoding_benchmark {
($name:tt) => {
fn $name() {
reth_db::kv::codecs::fuzz::Header::encode_and_decode(black_box(
reth_primitives::Header::default(),
));
}
main!($name);
};
}
#[cfg(not(feature = "bench-postcard"))]
impl_iai_encoding_benchmark!(scale);
#[cfg(feature = "bench-postcard")]
impl_iai_encoding_benchmark!(postcard);

View File

@ -0,0 +1,39 @@
//! Implements fuzzing targets to be used by test-fuzz
/// Fuzzer generates a random instance of the object and proceeds to encode and decode it. It then
/// makes sure that it matches the original object.
macro_rules! impl_fuzzer {
($($name:tt),+) => {
$(
/// Macro generated module to be used by test-fuzz and `bench` if it applies.
#[allow(non_snake_case)]
#[cfg(any(test, feature = "bench"))]
pub mod $name {
use reth_primitives::$name;
use crate::kv::table;
/// Encodes and decodes table types returning its encoded size and the decoded object.
pub fn encode_and_decode(obj: $name) -> (usize, $name) {
let data = table::Encode::encode(obj);
let size = data.len();
(size, table::Decode::decode(data).expect("failed to decode"))
}
#[cfg(test)]
#[allow(dead_code)]
#[test_fuzz::test_fuzz]
pub fn fuzz(obj: $name) {
assert!(encode_and_decode(obj.clone()).1 == obj );
}
#[test]
pub fn test() {
encode_and_decode($name::default());
}
}
)+
};
}
impl_fuzzer!(Header, Account);

View File

@ -1,2 +1,6 @@
//! Integrates different codecs into table::Encode and table::Decode
pub mod fuzz;
mod postcard;
#[cfg(not(feature = "bench-postcard"))]
mod scale;

View File

@ -1,9 +1,8 @@
#![allow(unused)]
use crate::kv::{Decode, Encode, KVError};
use heapless::Vec;
use postcard::{from_bytes, to_vec};
use reth_primitives::Account;
use postcard::{from_bytes, to_allocvec};
use reth_primitives::*;
// Just add `Serialize` and `Deserialize`, and set impl_heapless_postcard!(T, MaxSize(T))
//
@ -16,20 +15,27 @@ use reth_primitives::Account;
//
// impl_heapless_postcard!(T, MaxSize(T))
macro_rules! impl_heapless_postcard {
($name:tt, $static_size:tt) => {
impl Encode for $name {
type Encoded = Vec<u8, $static_size>;
macro_rules! impl_postcard {
($($name:tt),+) => {
$(
impl Encode for $name {
type Encoded = Vec<u8>;
fn encode(self) -> Self::Encoded {
to_vec(&self).expect("Failed to encode")
fn encode(self) -> Self::Encoded {
to_allocvec(&self).expect("Failed to encode")
}
}
}
impl Decode for $name {
fn decode<B: Into<bytes::Bytes>>(value: B) -> Result<Self, KVError> {
from_bytes(&value.into()).map_err(|_| KVError::InvalidValue)
impl Decode for $name {
fn decode<B: Into<bytes::Bytes>>(value: B) -> Result<Self, KVError> {
from_bytes(&value.into()).map_err(|_| KVError::InvalidValue)
}
}
}
)+
};
}
type VecU8 = Vec<u8>;
#[cfg(feature = "bench-postcard")]
impl_postcard!(VecU8, Receipt, H256, U256, H160, u8, u16, u64, Header, Account, Log, TxType);

View File

@ -24,7 +24,8 @@ use tx::Tx;
mod error;
pub use error::KVError;
mod codecs;
// Made public so `benches` can access it.
pub mod codecs;
/// Environment used when opening a MDBX environment. RO/RW.
#[derive(Debug)]

View File

@ -47,7 +47,7 @@ impl Encode for BlockNumHash {
let mut rnum = [0; 40];
rnum[..8].copy_from_slice(&number.to_be_bytes());
rnum[8..].copy_from_slice(&hash.encode());
rnum[8..].copy_from_slice(hash.as_bytes());
rnum
}
}

View File

@ -3,7 +3,7 @@ use reth_codecs::main_codec;
/// Account saved in database
#[main_codec]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct Account {
/// Nonce.
pub nonce: u64,