From ad56eb48b5ac9214fa6b82bf3f6ef59956c675e9 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 25 Feb 2020 11:25:02 +0100 Subject: [PATCH] Add benchmark for transaction execution (#11509) * Add benchmark for transaction execution * Address review grumbles * Address review grumbles and extend benches to test both blocks with both Constantinople and Istanbul rules --- Cargo.lock | 153 +++++++++++++------ ethcore/executive-state/Cargo.toml | 10 ++ ethcore/executive-state/benches/8481475.rlp | Bin 0 -> 32855 bytes ethcore/executive-state/benches/9532543.rlp | Bin 0 -> 38505 bytes ethcore/executive-state/benches/execution.rs | 150 ++++++++++++++++++ 5 files changed, 263 insertions(+), 50 deletions(-) create mode 100644 ethcore/executive-state/benches/8481475.rlp create mode 100644 ethcore/executive-state/benches/9532543.rlp create mode 100644 ethcore/executive-state/benches/execution.rs diff --git a/Cargo.lock b/Cargo.lock index eb82b87b9..3cbfb0638 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,6 +231,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "backtrace" version = "0.3.43" @@ -306,7 +312,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" dependencies = [ - "autocfg", + "autocfg 0.1.7", "byteorder", "serde", ] @@ -761,9 +767,9 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" +checksum = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" dependencies = [ "atty", "cast", @@ -773,10 +779,10 @@ dependencies = [ "itertools", "lazy_static", "num-traits 0.2.6", - "rand_core 0.5.1", - "rand_os 0.2.2", - "rand_xoshiro", + "oorandom", + "plotters", "rayon", + "regex", "serde", "serde_derive", "serde_json", @@ -786,9 +792,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" +checksum = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" dependencies = [ "cast", "itertools", @@ -800,18 +806,19 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" dependencies = [ - "crossbeam-epoch", + "crossbeam-epoch 0.7.2", "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-deque" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ - "crossbeam-epoch", - "crossbeam-utils 0.6.6", + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -828,6 +835,21 @@ dependencies = [ "scopeguard 1.0.0", ] +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg 1.0.0", + "cfg-if", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard 1.0.0", +] + [[package]] name = "crossbeam-queue" version = "0.1.2" @@ -837,6 +859,16 @@ dependencies = [ "crossbeam-utils 0.6.6", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +dependencies = [ + "cfg-if", + "crossbeam-utils 0.7.2", +] + [[package]] name = "crossbeam-utils" version = "0.5.0" @@ -853,6 +885,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.0.0", + "cfg-if", + "lazy_static", +] + [[package]] name = "crunchy" version = "0.1.6" @@ -1805,12 +1848,15 @@ dependencies = [ "account-db", "account-state", "common-types", + "criterion", "env_logger 0.5.13", "ethcore", + "ethcore-db", "ethereum-types", "evm", "hash-db", "hex-literal", + "journaldb", "keccak-hash", "keccak-hasher 0.1.1", "kvdb", @@ -1821,6 +1867,8 @@ dependencies = [ "patricia-trie-ethereum", "pod", "spec", + "state-db", + "tempdir", "trace", "trie-db", "trie-vm-factories", @@ -2090,7 +2138,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ "ahash", - "autocfg", + "autocfg 0.1.7", ] [[package]] @@ -2304,7 +2352,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" dependencies = [ - "autocfg", + "autocfg 0.1.7", ] [[package]] @@ -2799,6 +2847,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "memchr" version = "2.2.1" @@ -3140,6 +3194,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "oorandom" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" + [[package]] name = "opaque-debug" version = "0.2.2" @@ -3771,6 +3831,18 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "plotters" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +dependencies = [ + "js-sys", + "num-traits 0.2.6", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "pod" version = "0.1.0" @@ -4002,14 +4074,14 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg", + "autocfg 0.1.7", "libc", "rand_chacha 0.1.1", "rand_core 0.4.2", "rand_hc 0.1.0", "rand_isaac", "rand_jitter", - "rand_os 0.1.3", + "rand_os", "rand_pcg", "rand_xorshift 0.1.1", "winapi 0.3.8", @@ -4034,7 +4106,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.3.1", ] @@ -4133,23 +4205,13 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "rand_os" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" -dependencies = [ - "getrandom", - "rand_core 0.5.1", -] - [[package]] name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.4.2", ] @@ -4171,35 +4233,26 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_xoshiro" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rayon" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" dependencies = [ - "crossbeam-deque 0.6.3", + "crossbeam-deque 0.7.3", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" dependencies = [ - "crossbeam-deque 0.6.3", - "crossbeam-queue", - "crossbeam-utils 0.6.6", + "crossbeam-deque 0.7.3", + "crossbeam-queue 0.2.1", + "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", ] @@ -5255,8 +5308,8 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" dependencies = [ - "crossbeam-deque 0.7.1", - "crossbeam-queue", + "crossbeam-deque 0.7.3", + "crossbeam-queue 0.1.2", "crossbeam-utils 0.6.6", "futures", "lazy_static", @@ -5693,9 +5746,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.2.7" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", "winapi 0.3.8", diff --git a/ethcore/executive-state/Cargo.toml b/ethcore/executive-state/Cargo.toml index e1b5500b4..9c8006402 100644 --- a/ethcore/executive-state/Cargo.toml +++ b/ethcore/executive-state/Cargo.toml @@ -6,6 +6,10 @@ authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0" +[[bench]] +name = "execution" +harness = false + [dependencies] account-db = { path = "../account-db" } account-state = { path = "../account-state" } @@ -32,3 +36,9 @@ hex-literal = "0.2.1" spec = { path = "../spec" } trie-db = "0.20.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +# Benchmarks +criterion = "0.3.1" +ethcore-db = { path = "../db" } +journaldb = { path = "../../util/journaldb" } +state-db = { path = "../state-db" } +tempdir = "0.3.7" diff --git a/ethcore/executive-state/benches/8481475.rlp b/ethcore/executive-state/benches/8481475.rlp new file mode 100644 index 0000000000000000000000000000000000000000..ac41fd96637fc9aae73c42b75e460b9b79d6fc7c GIT binary patch literal 32855 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. + +//! Benchmark transaction execution of two blocks from mainnet, both of average +//! size (~35kb as RLP), one with 229 transactions (#8481475, Constantinople era) +//! and the other with 139 transactions (9532543, Istanbul era). Note that the +//! benchmark here is almost completely CPU-bound and does not involve IO at all, +//! so be careful not to draw too many conclusions from the results. + +use std::time::{Duration, Instant}; +use criterion::{Criterion, criterion_group, criterion_main}; + +use account_state::{CleanupMode, State}; +use common_types::{ + header::Header, + transaction::SignedTransaction, + verification::Unverified +}; +use ethcore::test_helpers::new_temp_db; +use ethcore_db as db; +use ethereum_types::U256; +use executive_state::ExecutiveState; +use spec::{new_constantinople_test_machine, new_istanbul_test_machine}; +use state_db::StateDB; +use tempdir::TempDir; + +fn build_state() -> State { + let db_path = TempDir::new("execution-bench").unwrap(); + let db = new_temp_db(&db_path.path()); + let journal_db = journaldb::new(db.key_value().clone(), journaldb::Algorithm::OverlayRecent, db::COL_STATE); + let state_db = StateDB::new(journal_db, 25 * 1024 * 1024); + State::new(state_db, U256::zero(), Default::default()) +} + +fn setup_state_for_block(state: &mut State, block: Unverified) -> Vec { + block.transactions + .into_iter() + .map(|tx| tx.verify_unordered().expect("tx is from known-good block")) + .inspect(|tx| { + // Ensure we have enough cash to execute the transaction + let gas_cost = tx.gas * tx.gas_price; + state.add_balance(&tx.sender(), &(tx.value + gas_cost), CleanupMode::ForceCreate).unwrap(); + // Fix up the nonce such that the state has the expected nonce + if state.nonce(&tx.sender()).unwrap() == U256::zero() { + for _ in 0..tx.nonce.as_usize() { + state.inc_nonce(&tx.sender()).unwrap(); + } + } + }) + .collect::>() + +} +fn build_env_info(header: &Header) -> vm::EnvInfo { + vm::EnvInfo { + number: header.number(), + author: *header.author(), + timestamp: header.timestamp(), + difficulty: *header.difficulty(), + gas_limit: *header.gas_limit() * 10, + last_hashes: std::sync::Arc::new(vec![]), + gas_used: *header.gas_used(), + } +} + +macro_rules! bench_tx_apply { + ($b: expr, $state: expr, $env_info: expr, $machine: expr, $signed_txs: expr, tracing => $tracing: expr ) => { + $b.iter_custom(|iters| { + let mut dur = Duration::new(0, 0); + for _ in 0..iters { + $state.checkpoint(); + let start = Instant::now(); + for tx in &$signed_txs { + let outcome = $state.apply(&$env_info, &$machine, tx, $tracing); + assert!(outcome.is_ok()) + } + dur += start.elapsed(); + $state.revert_to_checkpoint(); + } + dur + }) + } +} + +fn execute_8481475(c: &mut Criterion) { + // Block from the Constantinople era; 202 transactions, 32k RLP + let constantinople_block = Unverified::from_rlp(include_bytes!("./8481475.rlp").to_vec()).unwrap(); + let mut state = build_state(); + let env_info = build_env_info(&constantinople_block.header); + let signed_txs = setup_state_for_block(&mut state, constantinople_block); + + let machine = new_constantinople_test_machine(); + c.bench_function("Block 8481475, apply txs (Costantinople, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 8481475, apply txs (Costantinople, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); + + let machine = new_istanbul_test_machine(); + c.bench_function("Block 8481475, apply txs (Istanbul, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 8481475, apply txs (Istanbul, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); +} + +fn execute_9532543(c: &mut Criterion) { + // Block from the Istanbul era; 139 transactions, 38k RLP + let istanbul_block = Unverified::from_rlp(include_bytes!("./9532543.rlp").to_vec()).unwrap(); + let mut state = build_state(); + let env_info = build_env_info(&istanbul_block.header); + let signed_txs = setup_state_for_block(&mut state, istanbul_block); + + let machine = new_constantinople_test_machine(); + c.bench_function("Block 9532543, apply txs (Constantinople, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 9532543, apply txs (Constantinople, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); + + let machine = new_istanbul_test_machine(); + c.bench_function("Block 9532543, apply txs (Istanbul, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 9532543, apply txs (Istanbul, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); +} + +criterion_group!(benches, execute_8481475, execute_9532543); +criterion_main!(benches);