lighthouse-pulse/consensus/tree_hash/benches/benches.rs
Paul Hauner 21bcc8848d Add caching for state.eth1_data_votes (#919)
## Issue Addressed

NA

## Proposed Changes

Adds additional tree hash caching for `state.eth1_data_votes`.

Presently, each time we tree hash the `BeaconState`, we recompute the `state.eth1_data_votes` tree in it's entirety. This is because we only previous had support for caching fixed-length lists.

This PR adds the `Eth1DataVotesTreeHashCache` which provides caching for the `state.eth1_data_votes` list. The cache is aware of `SLOTS_PER_ETH1_VOTING_PERIOD` and will reset itself whenever that boundary is crossed.

This cache adds a new (but somewhat fundamental) restriction to tree hash caching:

*For some state `s`, `s.tree_hash_cache` is only valid for `s` or descendants of `s` that have been reached via state transitions that are faithful to the specification (invalid blocks are permitted, as long as they are faithfully processed).*
2020-07-24 02:19:47 +00:00

95 lines
3.2 KiB
Rust

use criterion::Criterion;
use criterion::{black_box, criterion_group, criterion_main, Benchmark};
use lazy_static::lazy_static;
use types::test_utils::{generate_deterministic_keypairs, TestingBeaconStateBuilder};
use types::{BeaconState, EthSpec, Keypair, MainnetEthSpec, MinimalEthSpec};
lazy_static! {
static ref KEYPAIRS: Vec<Keypair> = generate_deterministic_keypairs(300_000);
}
fn build_state<T: EthSpec>(validator_count: usize) -> BeaconState<T> {
let (state, _keypairs) = TestingBeaconStateBuilder::from_keypairs(
KEYPAIRS[0..validator_count].to_vec(),
&T::default_spec(),
)
.build();
assert_eq!(state.validators.len(), validator_count);
assert_eq!(state.balances.len(), validator_count);
assert!(state.previous_epoch_attestations.is_empty());
assert!(state.current_epoch_attestations.is_empty());
assert!(state.eth1_data_votes.is_empty());
assert!(state.historical_roots.is_empty());
state
}
// Note: `state.canonical_root()` uses whatever `tree_hash` that the `types` crate
// uses, which is not necessarily this crate. If you want to ensure that types is
// using this local version of `tree_hash`, ensure you add a workspace-level
// [dependency
// patch](https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section).
fn bench_suite<T: EthSpec>(c: &mut Criterion, spec_desc: &str, validator_count: usize) {
let state1 = build_state::<T>(validator_count);
let state2 = state1.clone();
let mut state3 = state1.clone();
state3.update_tree_hash_cache().unwrap();
c.bench(
&format!("{}/{}_validators/no_cache", spec_desc, validator_count),
Benchmark::new("genesis_state", move |b| {
b.iter_batched_ref(
|| state1.clone(),
|state| black_box(state.canonical_root()),
criterion::BatchSize::SmallInput,
)
})
.sample_size(10),
);
c.bench(
&format!("{}/{}_validators/empty_cache", spec_desc, validator_count),
Benchmark::new("genesis_state", move |b| {
b.iter_batched_ref(
|| state2.clone(),
|state| {
assert!(state.tree_hash_cache.is_none());
black_box(state.update_tree_hash_cache().unwrap())
},
criterion::BatchSize::SmallInput,
)
})
.sample_size(10),
);
c.bench(
&format!(
"{}/{}_validators/up_to_date_cache",
spec_desc, validator_count
),
Benchmark::new("genesis_state", move |b| {
b.iter_batched_ref(
|| state3.clone(),
|state| {
assert!(state.tree_hash_cache.is_some());
black_box(state.update_tree_hash_cache().unwrap())
},
criterion::BatchSize::SmallInput,
)
})
.sample_size(10),
);
}
fn all_benches(c: &mut Criterion) {
bench_suite::<MinimalEthSpec>(c, "minimal", 100_000);
bench_suite::<MinimalEthSpec>(c, "minimal", 300_000);
bench_suite::<MainnetEthSpec>(c, "mainnet", 100_000);
bench_suite::<MainnetEthSpec>(c, "mainnet", 300_000);
}
criterion_group!(benches, all_benches,);
criterion_main!(benches);