From 630baf5d703173e86f780b8a2f137a7131f416c5 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Thu, 20 Oct 2022 04:08:07 +0800 Subject: [PATCH] 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 --- Cargo.lock | 249 +++++++++++++++++++++++++++- crates/.gitignore | 1 + crates/codecs/derive/src/lib.rs | 2 +- crates/db/Cargo.toml | 16 +- crates/db/benches/README.md | 15 ++ crates/db/benches/encoding_crit.rs | 32 ++++ crates/db/benches/encoding_iai.rs | 20 +++ crates/db/src/kv/codecs/fuzz.rs | 39 +++++ crates/db/src/kv/codecs/mod.rs | 4 + crates/db/src/kv/codecs/postcard.rs | 34 ++-- crates/db/src/kv/mod.rs | 3 +- crates/db/src/kv/models/blocks.rs | 2 +- crates/db/src/static.rs | 0 crates/primitives/src/account.rs | 2 +- 14 files changed, 396 insertions(+), 23 deletions(-) create mode 100644 crates/.gitignore create mode 100644 crates/db/benches/README.md create mode 100644 crates/db/benches/encoding_crit.rs create mode 100644 crates/db/benches/encoding_iai.rs create mode 100644 crates/db/src/kv/codecs/fuzz.rs delete mode 100644 crates/db/src/static.rs diff --git a/Cargo.lock b/Cargo.lock index 98b83077d3..a3d1cea9d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.60.1" @@ -309,6 +318,37 @@ dependencies = [ "serde", ] +[[package]] +name = "camino" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.14", + "serde", + "serde_json", +] + [[package]] name = "cast" version = "0.3.0" @@ -655,6 +695,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "der" version = "0.6.0" @@ -1243,6 +1318,15 @@ dependencies = [ "digest 0.10.5", ] +[[package]] +name = "home" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +dependencies = [ + "winapi", +] + [[package]] name = "http" version = "0.2.8" @@ -1317,6 +1401,24 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "iai" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "impl-codec" version = "0.6.0" @@ -1980,6 +2082,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pest" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +dependencies = [ + "thiserror", + "ucd-trie", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -2302,7 +2414,8 @@ name = "reth-db" version = "0.1.0" dependencies = [ "bytes", - "heapless", + "criterion", + "iai", "libmdbx", "page_size", "parity-scale-codec", @@ -2310,6 +2423,7 @@ dependencies = [ "reth-primitives", "serde", "tempfile", + "test-fuzz", "thiserror", ] @@ -2849,7 +2963,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -2857,6 +2980,9 @@ name = "semver" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -2864,6 +2990,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "send_wrapper" version = "0.4.0" @@ -2914,6 +3049,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "sha2" version = "0.10.6" @@ -2997,7 +3143,7 @@ dependencies = [ "httparse", "log", "rand", - "sha-1", + "sha-1 0.9.8", ] [[package]] @@ -3065,6 +3211,16 @@ dependencies = [ "syn", ] +[[package]] +name = "subprocess" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "substrate-bn" version = "0.6.0" @@ -3124,6 +3280,63 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-fuzz" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125df852011c4f8f31df5620f4aea38ecddb5dfb4d9bc569b30485b15ffc3d4e" +dependencies = [ + "serde", + "test-fuzz-internal", + "test-fuzz-macro", + "test-fuzz-runtime", +] + +[[package]] +name = "test-fuzz-internal" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58071dc2471840e9f374eeb0f6e405a31bccb3cc5d59bb4598f02cafc274b5c4" +dependencies = [ + "cargo_metadata", + "proc-macro2", + "quote", + "serde", + "strum_macros", +] + +[[package]] +name = "test-fuzz-macro" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856bbca0314c328004691b9c0639fb198ca764d1ce0e20d4dd8b78f2697c2a6f" +dependencies = [ + "darling", + "if_chain", + "lazy_static", + "proc-macro2", + "quote", + "subprocess", + "syn", + "test-fuzz-internal", + "toolchain_find", + "unzip-n", +] + +[[package]] +name = "test-fuzz-runtime" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303774eb17994c2ddb59c460369f4c3a55496f013380278d78eeebd2deb896ac" +dependencies = [ + "bincode", + "hex", + "num-traits", + "serde", + "sha-1 0.10.0", + "test-fuzz-internal", +] + [[package]] name = "textwrap" version = "0.15.1" @@ -3246,6 +3459,19 @@ dependencies = [ "serde", ] +[[package]] +name = "toolchain_find" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e85654a10e7a07a47c6f19d93818f3f343e22927f2fa280c84f7c8042743413" +dependencies = [ + "home", + "lazy_static", + "regex", + "semver 0.11.0", + "walkdir", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -3306,6 +3532,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + [[package]] name = "uint" version = "0.9.4" @@ -3345,6 +3577,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "unzip-n" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "vcell" version = "0.1.3" diff --git a/crates/.gitignore b/crates/.gitignore new file mode 100644 index 0000000000..2f7896d1d1 --- /dev/null +++ b/crates/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/crates/codecs/derive/src/lib.rs b/crates/codecs/derive/src/lib.rs index 5dd662a391..dd9b126025 100644 --- a/crates/codecs/derive/src/lib.rs +++ b/crates/codecs/derive/src/lib.rs @@ -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() diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index 7232b36d3a..b902f9ef90 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -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 \ No newline at end of file diff --git a/crates/db/benches/README.md b/crates/db/benches/README.md new file mode 100644 index 0000000000..408211536c --- /dev/null +++ b/crates/db/benches/README.md @@ -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 +``` diff --git a/crates/db/benches/encoding_crit.rs b/crates/db/benches/encoding_crit.rs new file mode 100644 index 0000000000..27bbe36f7e --- /dev/null +++ b/crates/db/benches/encoding_crit.rs @@ -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); diff --git a/crates/db/benches/encoding_iai.rs b/crates/db/benches/encoding_iai.rs new file mode 100644 index 0000000000..b418f597c6 --- /dev/null +++ b/crates/db/benches/encoding_iai.rs @@ -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); diff --git a/crates/db/src/kv/codecs/fuzz.rs b/crates/db/src/kv/codecs/fuzz.rs new file mode 100644 index 0000000000..e8e50d42f9 --- /dev/null +++ b/crates/db/src/kv/codecs/fuzz.rs @@ -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); diff --git a/crates/db/src/kv/codecs/mod.rs b/crates/db/src/kv/codecs/mod.rs index 8869d6ad96..372ebe7412 100644 --- a/crates/db/src/kv/codecs/mod.rs +++ b/crates/db/src/kv/codecs/mod.rs @@ -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; diff --git a/crates/db/src/kv/codecs/postcard.rs b/crates/db/src/kv/codecs/postcard.rs index 171ee5ea06..6e3b141d70 100644 --- a/crates/db/src/kv/codecs/postcard.rs +++ b/crates/db/src/kv/codecs/postcard.rs @@ -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; +macro_rules! impl_postcard { + ($($name:tt),+) => { + $( + impl Encode for $name { + type Encoded = Vec; - 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>(value: B) -> Result { - from_bytes(&value.into()).map_err(|_| KVError::InvalidValue) + impl Decode for $name { + fn decode>(value: B) -> Result { + from_bytes(&value.into()).map_err(|_| KVError::InvalidValue) + } } - } + )+ }; } + +type VecU8 = Vec; + +#[cfg(feature = "bench-postcard")] +impl_postcard!(VecU8, Receipt, H256, U256, H160, u8, u16, u64, Header, Account, Log, TxType); diff --git a/crates/db/src/kv/mod.rs b/crates/db/src/kv/mod.rs index 67740a7839..1e2fcab555 100644 --- a/crates/db/src/kv/mod.rs +++ b/crates/db/src/kv/mod.rs @@ -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)] diff --git a/crates/db/src/kv/models/blocks.rs b/crates/db/src/kv/models/blocks.rs index 4e319373e8..2142340605 100644 --- a/crates/db/src/kv/models/blocks.rs +++ b/crates/db/src/kv/models/blocks.rs @@ -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 } } diff --git a/crates/db/src/static.rs b/crates/db/src/static.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index 4aeeedbd60..6e67349560 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -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,