Merge pull request #261 from sigp/epoch-benches
Move epoch benches to 16k validators
This commit is contained in:
commit
ed032dddea
@ -10,6 +10,7 @@ harness = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.2"
|
||||
state_processing = { path = "../../../eth2/state_processing" }
|
||||
|
||||
[dependencies]
|
||||
attester = { path = "../../../eth2/attester" }
|
||||
|
@ -1,6 +1,7 @@
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark};
|
||||
// use env_logger::{Builder, Env};
|
||||
use state_processing::SlotProcessable;
|
||||
use test_harness::BeaconChainHarness;
|
||||
use types::{ChainSpec, Hash256};
|
||||
|
||||
|
@ -1,27 +1,64 @@
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark};
|
||||
// use env_logger::{Builder, Env};
|
||||
use state_processing::SlotProcessable;
|
||||
use types::{beacon_state::BeaconStateBuilder, ChainSpec, Hash256};
|
||||
use types::beacon_state::BeaconStateBuilder;
|
||||
use types::*;
|
||||
|
||||
fn epoch_processing(c: &mut Criterion) {
|
||||
// Builder::from_env(Env::default().default_filter_or("debug")).init();
|
||||
|
||||
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||
builder.spec = ChainSpec::few_validators();
|
||||
let mut builder = BeaconStateBuilder::new(16_384);
|
||||
|
||||
builder.genesis().unwrap();
|
||||
builder.build_fast().unwrap();
|
||||
builder.teleport_to_end_of_epoch(builder.spec.genesis_epoch + 4);
|
||||
|
||||
let state = builder.build().unwrap();
|
||||
let mut state = builder.cloned_state();
|
||||
|
||||
c.bench_function("epoch processing", move |b| {
|
||||
let spec = &builder.spec;
|
||||
b.iter_with_setup(
|
||||
|| state.clone(),
|
||||
|mut state| black_box(state.per_slot_processing(Hash256::zero(), spec).unwrap()),
|
||||
)
|
||||
});
|
||||
// Build all the caches so the following state does _not_ include the cache-building time.
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::Previous, &builder.spec)
|
||||
.unwrap();
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::Current, &builder.spec)
|
||||
.unwrap();
|
||||
state
|
||||
.build_epoch_cache(RelativeEpoch::Next, &builder.spec)
|
||||
.unwrap();
|
||||
|
||||
let cached_state = state.clone();
|
||||
|
||||
// Drop all the caches so the following state includes the cache-building time.
|
||||
state.drop_cache(RelativeEpoch::Previous);
|
||||
state.drop_cache(RelativeEpoch::Current);
|
||||
state.drop_cache(RelativeEpoch::Next);
|
||||
|
||||
let cacheless_state = state;
|
||||
|
||||
let spec_a = builder.spec.clone();
|
||||
let spec_b = builder.spec.clone();
|
||||
|
||||
c.bench(
|
||||
"epoch processing",
|
||||
Benchmark::new("with pre-built caches", move |b| {
|
||||
b.iter_with_setup(
|
||||
|| cached_state.clone(),
|
||||
|mut state| black_box(state.per_slot_processing(Hash256::zero(), &spec_a).unwrap()),
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"epoch processing",
|
||||
Benchmark::new("without pre-built caches", move |b| {
|
||||
b.iter_with_setup(
|
||||
|| cacheless_state.clone(),
|
||||
|mut state| black_box(state.per_slot_processing(Hash256::zero(), &spec_b).unwrap()),
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, epoch_processing,);
|
||||
|
@ -8,13 +8,13 @@ use types::*;
|
||||
fn runs_without_error() {
|
||||
Builder::from_env(Env::default().default_filter_or("error")).init();
|
||||
|
||||
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||
let mut builder = BeaconStateBuilder::new(8);
|
||||
builder.spec = ChainSpec::few_validators();
|
||||
|
||||
builder.genesis().unwrap();
|
||||
builder.build().unwrap();
|
||||
builder.teleport_to_end_of_epoch(builder.spec.genesis_epoch + 4);
|
||||
|
||||
let mut state = builder.build().unwrap();
|
||||
let mut state = builder.cloned_state();
|
||||
|
||||
let spec = &builder.spec;
|
||||
state.per_epoch_processing(spec).unwrap();
|
||||
|
@ -117,19 +117,18 @@ pub struct BeaconState {
|
||||
|
||||
impl BeaconState {
|
||||
/// Produce the first state of the Beacon Chain.
|
||||
pub fn genesis(
|
||||
pub fn genesis_without_validators(
|
||||
genesis_time: u64,
|
||||
initial_validator_deposits: Vec<Deposit>,
|
||||
latest_eth1_data: Eth1Data,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState, Error> {
|
||||
debug!("Creating genesis state.");
|
||||
debug!("Creating genesis state (without validator processing).");
|
||||
let initial_crosslink = Crosslink {
|
||||
epoch: spec.genesis_epoch,
|
||||
shard_block_root: spec.zero_hash,
|
||||
};
|
||||
|
||||
let mut genesis_state = BeaconState {
|
||||
Ok(BeaconState {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
@ -188,7 +187,19 @@ impl BeaconState {
|
||||
*/
|
||||
cache_index_offset: 0,
|
||||
caches: vec![EpochCache::empty(); CACHED_EPOCHS],
|
||||
};
|
||||
})
|
||||
}
|
||||
/// Produce the first state of the Beacon Chain.
|
||||
pub fn genesis(
|
||||
genesis_time: u64,
|
||||
initial_validator_deposits: Vec<Deposit>,
|
||||
latest_eth1_data: Eth1Data,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState, Error> {
|
||||
let mut genesis_state =
|
||||
BeaconState::genesis_without_validators(genesis_time, latest_eth1_data, spec)?;
|
||||
|
||||
trace!("Processing genesis deposits...");
|
||||
|
||||
let deposit_data = initial_validator_deposits
|
||||
.iter()
|
||||
@ -279,14 +290,18 @@ impl BeaconState {
|
||||
/// -- `Next` epoch is always _without_ a registry change. If you perform a registry update,
|
||||
/// you should rebuild the `Current` cache so it uses the new seed.
|
||||
pub fn advance_caches(&mut self) {
|
||||
let previous_cache_index = self.cache_index(RelativeEpoch::Previous);
|
||||
|
||||
self.caches[previous_cache_index] = EpochCache::empty();
|
||||
self.drop_cache(RelativeEpoch::Previous);
|
||||
|
||||
self.cache_index_offset += 1;
|
||||
self.cache_index_offset %= CACHED_EPOCHS;
|
||||
}
|
||||
|
||||
/// Removes the specified cache and sets it to uninitialized.
|
||||
pub fn drop_cache(&mut self, relative_epoch: RelativeEpoch) {
|
||||
let previous_cache_index = self.cache_index(relative_epoch);
|
||||
self.caches[previous_cache_index] = EpochCache::empty();
|
||||
}
|
||||
|
||||
/// Returns the index of `self.caches` for some `RelativeEpoch`.
|
||||
fn cache_index(&self, relative_epoch: RelativeEpoch) -> usize {
|
||||
let base_index = match relative_epoch {
|
||||
|
@ -6,20 +6,19 @@ use bls::create_proof_of_possession;
|
||||
/// Building the `BeaconState` is a three step processes:
|
||||
///
|
||||
/// 1. Create a new `BeaconStateBuilder`.
|
||||
/// 2. Run the `genesis` function to generate a new BeaconState.
|
||||
/// 2. Call `Self::build()` or `Self::build_fast()` generate a `BeaconState`.
|
||||
/// 3. (Optional) Use builder functions to modify the `BeaconState`.
|
||||
/// 4. Call `build()` to obtain a cloned `BeaconState`.
|
||||
/// 4. Call `Self::cloned_state()` to obtain a `BeaconState` cloned from this struct.
|
||||
///
|
||||
/// Step (2) is necessary because step (3) requires an existing `BeaconState` object. (2) is not
|
||||
/// included in (1) to allow for modifying params before generating the `BeaconState` (e.g., the
|
||||
/// spec).
|
||||
/// Step (2) happens prior to step (3) because some functionality requires an existing
|
||||
/// `BeaconState`.
|
||||
///
|
||||
/// Step (4) produces a clone of the BeaconState and doesn't consume the `BeaconStateBuilder` to
|
||||
/// allow access to the `keypairs` and `spec`.
|
||||
/// allow access to `self.keypairs` and `self.spec`.
|
||||
pub struct BeaconStateBuilder {
|
||||
pub validator_count: usize,
|
||||
pub state: Option<BeaconState>,
|
||||
pub genesis_time: u64,
|
||||
pub initial_validator_deposits: Vec<Deposit>,
|
||||
pub latest_eth1_data: Eth1Data,
|
||||
pub spec: ChainSpec,
|
||||
pub keypairs: Vec<Keypair>,
|
||||
@ -27,21 +26,46 @@ pub struct BeaconStateBuilder {
|
||||
|
||||
impl BeaconStateBuilder {
|
||||
/// Create a new builder with the given number of validators.
|
||||
pub fn with_random_validators(validator_count: usize) -> Self {
|
||||
pub fn new(validator_count: usize) -> Self {
|
||||
let genesis_time = 10_000_000;
|
||||
let keypairs: Vec<Keypair> = (0..validator_count)
|
||||
|
||||
let latest_eth1_data = Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
};
|
||||
|
||||
let spec = ChainSpec::foundation();
|
||||
|
||||
Self {
|
||||
validator_count,
|
||||
state: None,
|
||||
genesis_time,
|
||||
latest_eth1_data,
|
||||
spec,
|
||||
keypairs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BeaconState` using the `BeaconState::genesis(..)` function.
|
||||
///
|
||||
/// Each validator is assigned a unique, randomly-generated keypair and all
|
||||
/// proof-of-possessions are verified during genesis.
|
||||
pub fn build(&mut self) -> Result<(), BeaconStateError> {
|
||||
self.keypairs = (0..self.validator_count)
|
||||
.collect::<Vec<usize>>()
|
||||
.iter()
|
||||
.map(|_| Keypair::random())
|
||||
.collect();
|
||||
let initial_validator_deposits = keypairs
|
||||
|
||||
let initial_validator_deposits = self
|
||||
.keypairs
|
||||
.iter()
|
||||
.map(|keypair| Deposit {
|
||||
branch: vec![], // branch verification is not specified.
|
||||
index: 0, // index verification is not specified.
|
||||
deposit_data: DepositData {
|
||||
amount: 32_000_000_000, // 32 ETH (in Gwei)
|
||||
timestamp: genesis_time - 1,
|
||||
timestamp: self.genesis_time - 1,
|
||||
deposit_input: DepositInput {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
|
||||
@ -50,27 +74,10 @@ impl BeaconStateBuilder {
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
let latest_eth1_data = Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
};
|
||||
let spec = ChainSpec::foundation();
|
||||
|
||||
Self {
|
||||
state: None,
|
||||
genesis_time,
|
||||
initial_validator_deposits,
|
||||
latest_eth1_data,
|
||||
spec,
|
||||
keypairs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the `BeaconState::genesis` function and produces a `BeaconState`.
|
||||
pub fn genesis(&mut self) -> Result<(), BeaconStateError> {
|
||||
let state = BeaconState::genesis(
|
||||
self.genesis_time,
|
||||
self.initial_validator_deposits.clone(),
|
||||
initial_validator_deposits,
|
||||
self.latest_eth1_data.clone(),
|
||||
&self.spec,
|
||||
)?;
|
||||
@ -80,6 +87,49 @@ impl BeaconStateBuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a `BeaconState` using the `BeaconState::genesis(..)` function, without supplying any
|
||||
/// validators. Instead validators are added to the state post-genesis.
|
||||
///
|
||||
/// One keypair is randomly generated and all validators are assigned this same keypair.
|
||||
/// Proof-of-possessions are not created (or validated).
|
||||
///
|
||||
/// This function runs orders of magnitude faster than `Self::build()`, however it will be
|
||||
/// erroneous for functions which use a validators public key as an identifier (e.g.,
|
||||
/// deposits).
|
||||
pub fn build_fast(&mut self) -> Result<(), BeaconStateError> {
|
||||
let common_keypair = Keypair::random();
|
||||
|
||||
let mut validator_registry = Vec::with_capacity(self.validator_count);
|
||||
let mut validator_balances = Vec::with_capacity(self.validator_count);
|
||||
self.keypairs = Vec::with_capacity(self.validator_count);
|
||||
|
||||
for _ in 0..self.validator_count {
|
||||
self.keypairs.push(common_keypair.clone());
|
||||
validator_balances.push(32_000_000_000);
|
||||
validator_registry.push(Validator {
|
||||
pubkey: common_keypair.pk.clone(),
|
||||
withdrawal_credentials: Hash256::zero(),
|
||||
activation_epoch: self.spec.genesis_epoch,
|
||||
..Validator::default()
|
||||
})
|
||||
}
|
||||
|
||||
let state = BeaconState {
|
||||
validator_registry,
|
||||
validator_balances,
|
||||
..BeaconState::genesis(
|
||||
self.genesis_time,
|
||||
vec![],
|
||||
self.latest_eth1_data.clone(),
|
||||
&self.spec,
|
||||
)?
|
||||
};
|
||||
|
||||
self.state = Some(state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the `BeaconState` to be in the last slot of the given epoch.
|
||||
///
|
||||
/// Sets all justification/finalization parameters to be be as "perfect" as possible (i.e.,
|
||||
@ -145,11 +195,8 @@ impl BeaconStateBuilder {
|
||||
}
|
||||
|
||||
/// Returns a cloned `BeaconState`.
|
||||
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
|
||||
match &self.state {
|
||||
Some(state) => Ok(state.clone()),
|
||||
None => panic!("Genesis required"),
|
||||
}
|
||||
pub fn cloned_state(&self) -> BeaconState {
|
||||
self.state.as_ref().expect("Genesis required").clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,7 @@ use ssz::{ssz_encode, Decodable};
|
||||
|
||||
#[test]
|
||||
pub fn can_produce_genesis_block() {
|
||||
let mut builder = BeaconStateBuilder::with_random_validators(2);
|
||||
builder.genesis().unwrap();
|
||||
|
||||
let mut builder = BeaconStateBuilder::new(2);
|
||||
builder.build().unwrap();
|
||||
}
|
||||
|
||||
@ -19,12 +17,12 @@ pub fn can_produce_genesis_block() {
|
||||
pub fn get_attestation_participants_consistency() {
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
|
||||
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||
let mut builder = BeaconStateBuilder::new(8);
|
||||
builder.spec = ChainSpec::few_validators();
|
||||
|
||||
builder.genesis().unwrap();
|
||||
builder.build().unwrap();
|
||||
|
||||
let mut state = builder.build().unwrap();
|
||||
let mut state = builder.cloned_state();
|
||||
let spec = builder.spec.clone();
|
||||
|
||||
state
|
||||
|
Loading…
Reference in New Issue
Block a user