Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Benchmarks for block verification (#11035)
Browse files Browse the repository at this point in the history
* WIP

* wip

* Benchmarks for block verification

Uses real blocks from mainnet to benchmark the `verify_*` family of methods in the `verification` module.

Also exposes the `TestBlockChain` in a test helper.

* Cleanup, fix CI

* Bash syntax error

* One more try

* Fix review grumbles
	Revert unwanted changes
	Tweak CI benchmark checks
  • Loading branch information
dvdplm authored and ordian committed Sep 11, 2019
1 parent 48629c2 commit f4d14e2
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 109 deletions.
7 changes: 3 additions & 4 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ cargo-check-benches:
stage: test
<<: *docker-cache-status
script:
- time (
cargo check --all --benches --exclude ethash --target $CARGO_TARGET --locked --verbose --color=always;
(cd ethash; time cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always)
)
- time (cargo check --all --benches --exclude ethash --exclude verification --target $CARGO_TARGET --locked --verbose --color=always)
- time (cd ethash; cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always)
- time (cd ethcore/verification; cargo check --benches --features bench --target $CARGO_TARGET --locked --verbose --color=always)
- sccache -s

cargo-audit:
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ethcore/types/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl VerificationQueueInfo {
}

/// An unverified block.
#[derive(PartialEq, Debug, MallocSizeOf)]
#[derive(Clone, PartialEq, Debug, MallocSizeOf)]
pub struct Unverified {
/// Unverified block header.
pub header: Header,
Expand Down
13 changes: 13 additions & 0 deletions ethcore/verification/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "GPL-3.0"

[[bench]]
name = "verification"
harness = false

[dependencies]
blockchain = { package = "ethcore-blockchain", path = "../blockchain" }
call-contract = { package = "ethcore-call-contract", path = "../call-contract" }
Expand All @@ -27,8 +31,17 @@ triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util
unexpected = { path = "../../util/unexpected" }

[dev-dependencies]
criterion = "0.3"
ethcore = { path = "../", features = ["test-helpers"] }
ethkey = { path = "../../accounts/ethkey" }
machine = { path = "../machine" }
null-engine = { path = "../engines/null-engine" }
spec = { path = "../spec" }

# Benches
ethash = { package = "ethash-engine", path = "../engines/ethash" }
tempdir = "0.3.7"

[features]
# Used to selectively expose code for benchmarks.
bench = []
Binary file added ethcore/verification/benches/8447675.rlp
Binary file not shown.
Binary file not shown.
Binary file added ethcore/verification/benches/8481475.rlp
Binary file not shown.
Binary file not shown.
161 changes: 161 additions & 0 deletions ethcore/verification/benches/verification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// 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 <http://www.gnu.org/licenses/>.

//! benchmarking for verification

use std::collections::BTreeMap;

use common_types::verification::Unverified;
use criterion::{Criterion, criterion_group, criterion_main};
use ethash::{EthashParams, Ethash};
use ethereum_types::U256;
use ethcore::client::TestBlockChainClient;
use spec::new_constantinople_test_machine;
use tempdir::TempDir;

use ::verification::{
FullFamilyParams,
verification,
test_helpers::TestBlockChain,
};

// These are current production values. Needed when using real blocks.
fn ethash_params() -> EthashParams {
EthashParams {
minimum_difficulty: U256::from(131072),
difficulty_bound_divisor: U256::from(2048),
difficulty_increment_divisor: 10,
metropolis_difficulty_increment_divisor: 9,
duration_limit: 13,
homestead_transition: 1150000,
difficulty_hardfork_transition: u64::max_value(),
difficulty_hardfork_bound_divisor: U256::from(2048),
bomb_defuse_transition: u64::max_value(),
eip100b_transition: 4370000,
ecip1010_pause_transition: u64::max_value(),
ecip1010_continue_transition: u64::max_value(),
ecip1017_era_rounds: u64::max_value(),
block_reward: {
let mut m = BTreeMap::<u64, U256>::new();
m.insert(0, 5000000000000000000u64.into());
m.insert(4370000, 3000000000000000000u64.into());
m.insert(7280000, 2000000000000000000u64.into());
m
},
expip2_transition: u64::max_value(),
expip2_duration_limit: 30,
block_reward_contract_transition: 0,
block_reward_contract: None,
difficulty_bomb_delays: {
let mut m = BTreeMap::new();
m.insert(4370000, 3000000);
m.insert(7280000, 2000000);
m
},
progpow_transition: u64::max_value()
}
}

fn build_ethash() -> Ethash {
let machine = new_constantinople_test_machine();
let ethash_params = ethash_params();
let cache_dir = TempDir::new("").unwrap();
Ethash::new(
cache_dir.path(),
ethash_params,
machine,
None
)
}

fn block_verification(c: &mut Criterion) {
const PROOF: &str = "bytes from disk are ok";

let ethash = build_ethash();

// A fairly large block (32kb) with one uncle
let rlp_8481476 = include_bytes!("./8481476-one-uncle.rlp").to_vec();
// Parent of #8481476
let rlp_8481475 = include_bytes!("./8481475.rlp").to_vec();
// Parent of the uncle in #8481476
let rlp_8481474 = include_bytes!("./8481474-parent-to-uncle.rlp").to_vec();

// Phase 1 verification
c.bench_function("verify_block_basic", |b| {
let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF);
b.iter(|| {
assert!(verification::verify_block_basic(
&block,
&ethash,
true
).is_ok());
})
});

// Phase 2 verification
c.bench_function("verify_block_unordered", |b| {
let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF);
b.iter( || {
assert!(verification::verify_block_unordered(
block.clone(),
&ethash,
true
).is_ok());
})
});

// Phase 3 verification
let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF);
let preverified = verification::verify_block_unordered(block, &ethash, true).expect(PROOF);
let parent = Unverified::from_rlp(rlp_8481475.clone()).expect(PROOF);

// "partial" means we skip uncle and tx verification
c.bench_function("verify_block_family (partial)", |b| {
b.iter(|| {
if let Err(e) = verification::verify_block_family::<TestBlockChainClient>(
&preverified.header,
&parent.header,
&ethash,
None
) {
panic!("verify_block_family (partial) ERROR: {:?}", e);
}
});
});

let mut block_provider = TestBlockChain::new();
block_provider.insert(rlp_8481476.clone()); // block to verify
block_provider.insert(rlp_8481475.clone()); // parent
block_provider.insert(rlp_8481474.clone()); // uncle's parent

let client = TestBlockChainClient::default();
c.bench_function("verify_block_family (full)", |b| {
b.iter(|| {
let full = FullFamilyParams { block: &preverified, block_provider: &block_provider, client: &client };
if let Err(e) = verification::verify_block_family::<TestBlockChainClient>(
&preverified.header,
&parent.header,
&ethash,
Some(full),
) {
panic!("verify_block_family (full) ERROR: {:?}", e)
}
});
});
}

criterion_group!(benches, block_verification);
criterion_main!(benches);
5 changes: 5 additions & 0 deletions ethcore/verification/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ use client_traits::BlockInfo;
// The MallocSizeOf derive looks for this in the root
use parity_util_mem as malloc_size_of;

#[cfg(feature = "bench" )]
pub mod verification;
#[cfg(not(feature = "bench" ))]
mod verification;
mod verifier;
pub mod queue;
mod canon_verifier;
mod noop_verifier;
#[cfg(any(test, feature = "bench" ))]
pub mod test_helpers;

pub use self::verification::FullFamilyParams;
pub use self::verifier::Verifier;
Expand Down
114 changes: 114 additions & 0 deletions ethcore/verification/src/test_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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 <http://www.gnu.org/licenses/>.

//! Verification test helpers.

use std::collections::HashMap;

use blockchain::{BlockProvider, BlockChain, BlockDetails, TransactionAddress, BlockReceipts};
use common_types::{
BlockNumber,
encoded,
verification::Unverified,
log_entry::{LogEntry, LocalizedLogEntry},
};
use ethereum_types::{BloomRef, H256};
use parity_bytes::Bytes;

#[derive(Default)]
pub struct TestBlockChain {
blocks: HashMap<H256, Bytes>,
numbers: HashMap<BlockNumber, H256>,
}

impl TestBlockChain {
pub fn new() -> Self { TestBlockChain::default() }

pub fn insert(&mut self, bytes: Bytes) {
let header = Unverified::from_rlp(bytes.clone()).unwrap().header;
let hash = header.hash();
self.blocks.insert(hash, bytes);
self.numbers.insert(header.number(), hash);
}
}

impl BlockProvider for TestBlockChain {
fn is_known(&self, hash: &H256) -> bool {
self.blocks.contains_key(hash)
}

fn first_block(&self) -> Option<H256> {
unimplemented!()
}

fn best_ancient_block(&self) -> Option<H256> {
None
}

/// Get raw block data
fn block(&self, hash: &H256) -> Option<encoded::Block> {
self.blocks.get(hash).cloned().map(encoded::Block::new)
}

/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.blocks.get(hash).map(|bytes| {
let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header;
BlockDetails {
number: header.number(),
total_difficulty: *header.difficulty(),
parent: *header.parent_hash(),
children: Vec::new(),
is_finalized: false,
}
})
}

/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.numbers.get(&index).cloned()
}

fn transaction_address(&self, _hash: &H256) -> Option<TransactionAddress> {
unimplemented!()
}

fn block_receipts(&self, _hash: &H256) -> Option<BlockReceipts> {
unimplemented!()
}

fn block_header_data(&self, hash: &H256) -> Option<encoded::Header> {
self.block(hash)
.map(|b| b.header_view().rlp().as_raw().to_vec())
.map(encoded::Header::new)
}

fn block_body(&self, hash: &H256) -> Option<encoded::Body> {
self.block(hash)
.map(|b| BlockChain::block_to_body(&b.into_inner()))
.map(encoded::Body::new)
}

fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec<BlockNumber>
where BloomRef<'a>: From<B>, II: IntoIterator<Item = B, IntoIter = I> + Copy, I: Iterator<Item = B>, Self: Sized {
unimplemented!()
}

fn logs<F>(&self, _blocks: Vec<H256>, _matches: F, _limit: Option<usize>) -> Vec<LocalizedLogEntry>
where F: Fn(&LogEntry) -> bool, Self: Sized {
unimplemented!()
}
}
Loading

0 comments on commit f4d14e2

Please sign in to comment.