Skip to content

Commit

Permalink
perf(engine): include selfdestructs in StateRootTask bench input (#13293
Browse files Browse the repository at this point in the history
)

Co-authored-by: Alexey Shekhirin <[email protected]>
  • Loading branch information
fgimenez and shekhirin authored Dec 20, 2024
1 parent 3c4a0dc commit 37ef201
Showing 1 changed file with 117 additions and 51 deletions.
168 changes: 117 additions & 51 deletions crates/engine/tree/benches/state_root_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use reth_primitives::{Account as RethAccount, StorageEntry};
use reth_provider::{
providers::ConsistentDbView,
test_utils::{create_test_provider_factory, MockNodeTypesWithDB},
HashingWriter, ProviderFactory,
AccountReader, HashingWriter, ProviderFactory,
};
use reth_testing_utils::generators::{self, Rng};
use reth_trie::{
Expand All @@ -28,38 +28,54 @@ struct BenchParams {
num_accounts: usize,
updates_per_account: usize,
storage_slots_per_account: usize,
selfdestructs_per_update: usize,
}

/// Generates a series of random state updates with configurable accounts,
/// storage, and self-destructs
fn create_bench_state_updates(params: &BenchParams) -> Vec<EvmState> {
let mut rng = generators::rng();
let all_addresses: Vec<Address> = (0..params.num_accounts).map(|_| rng.gen()).collect();
let mut updates = Vec::new();

for _ in 0..params.updates_per_account {
let num_accounts_in_update = rng.gen_range(1..=params.num_accounts);
let mut state_update = EvmState::default();
let num_accounts_in_update = rng.gen_range(1..=params.num_accounts);

let selected_addresses = &all_addresses[0..num_accounts_in_update];

for &address in selected_addresses {
let mut storage = HashMap::default();
for _ in 0..params.storage_slots_per_account {
let slot = U256::from(rng.gen::<u64>());
storage.insert(
slot,
EvmStorageSlot::new_changed(U256::ZERO, U256::from(rng.gen::<u64>())),
);
}
// regular updates for randomly selected accounts
for &address in &all_addresses[0..num_accounts_in_update] {
// randomly choose to self-destruct with probability
// (selfdestructs/accounts)
let is_selfdestruct =
rng.gen_bool(params.selfdestructs_per_update as f64 / params.num_accounts as f64);

let account = RevmAccount {
info: AccountInfo {
balance: U256::from(rng.gen::<u64>()),
nonce: rng.gen::<u64>(),
code_hash: KECCAK_EMPTY,
code: Some(Default::default()),
},
storage,
status: AccountStatus::Touched,
let account = if is_selfdestruct {
RevmAccount {
info: AccountInfo::default(),
storage: HashMap::default(),
status: AccountStatus::SelfDestructed,
}
} else {
RevmAccount {
info: AccountInfo {
balance: U256::from(rng.gen::<u64>()),
nonce: rng.gen::<u64>(),
code_hash: KECCAK_EMPTY,
code: Some(Default::default()),
},
storage: (0..rng.gen_range(0..=params.storage_slots_per_account))
.map(|_| {
(
U256::from(rng.gen::<u64>()),
EvmStorageSlot::new_changed(
U256::ZERO,
U256::from(rng.gen::<u64>()),
),
)
})
.collect(),
status: AccountStatus::Touched,
}
};

state_update.insert(address, account);
Expand All @@ -71,61 +87,111 @@ fn create_bench_state_updates(params: &BenchParams) -> Vec<EvmState> {
updates
}

fn convert_revm_to_reth_account(revm_account: &RevmAccount) -> RethAccount {
RethAccount {
balance: revm_account.info.balance,
nonce: revm_account.info.nonce,
bytecode_hash: if revm_account.info.code_hash == KECCAK_EMPTY {
None
} else {
Some(revm_account.info.code_hash)
},
fn convert_revm_to_reth_account(revm_account: &RevmAccount) -> Option<RethAccount> {
match revm_account.status {
AccountStatus::SelfDestructed => None,
_ => Some(RethAccount {
balance: revm_account.info.balance,
nonce: revm_account.info.nonce,
bytecode_hash: if revm_account.info.code_hash == KECCAK_EMPTY {
None
} else {
Some(revm_account.info.code_hash)
},
}),
}
}

/// Applies state updates to the provider, ensuring self-destructs only affect
/// existing accounts
fn setup_provider(
factory: &ProviderFactory<MockNodeTypesWithDB>,
state_updates: &[EvmState],
) -> Result<(), Box<dyn std::error::Error>> {
let provider_rw = factory.provider_rw()?;

for update in state_updates {
let account_updates = update
.iter()
.map(|(address, account)| (*address, Some(convert_revm_to_reth_account(account))));
provider_rw.insert_account_for_hashing(account_updates)?;

let storage_updates = update.iter().map(|(address, account)| {
let storage_entries = account.storage.iter().map(|(slot, value)| StorageEntry {
key: B256::from(*slot),
value: value.present_value,
});
(*address, storage_entries)
});
provider_rw.insert_storage_for_hashing(storage_updates)?;
let provider_rw = factory.provider_rw()?;

let mut account_updates = Vec::new();

for (address, account) in update {
// only process self-destructs if account exists, always process
// other updates
let should_process = match account.status {
AccountStatus::SelfDestructed => {
provider_rw.basic_account(*address).ok().flatten().is_some()
}
_ => true,
};

if should_process {
account_updates.push((
*address,
convert_revm_to_reth_account(account),
(account.status == AccountStatus::Touched).then(|| {
account
.storage
.iter()
.map(|(slot, value)| StorageEntry {
key: B256::from(*slot),
value: value.present_value,
})
.collect::<Vec<_>>()
}),
));
}
}

// update in the provider account and its storage (if available)
for (address, account, maybe_storage) in account_updates {
provider_rw.insert_account_for_hashing(std::iter::once((address, account)))?;
if let Some(storage) = maybe_storage {
provider_rw
.insert_storage_for_hashing(std::iter::once((address, storage.into_iter())))?;
}
}

provider_rw.commit()?;
}

provider_rw.commit()?;
Ok(())
}

fn bench_state_root(c: &mut Criterion) {
reth_tracing::init_test_tracing();

let mut group = c.benchmark_group("state_root");

let scenarios = vec![
BenchParams { num_accounts: 100, updates_per_account: 5, storage_slots_per_account: 10 },
BenchParams { num_accounts: 1000, updates_per_account: 10, storage_slots_per_account: 20 },
BenchParams {
num_accounts: 100,
updates_per_account: 5,
storage_slots_per_account: 10,
selfdestructs_per_update: 2,
},
BenchParams {
num_accounts: 1000,
updates_per_account: 10,
storage_slots_per_account: 20,
selfdestructs_per_update: 5,
},
BenchParams {
num_accounts: 500,
updates_per_account: 8,
storage_slots_per_account: 15,
selfdestructs_per_update: 20,
},
];

for params in scenarios {
group.bench_with_input(
BenchmarkId::new(
"state_root_task",
format!(
"accounts_{}_updates_{}_slots_{}",
"accounts_{}_updates_{}_slots_{}_selfdestructs_{}",
params.num_accounts,
params.updates_per_account,
params.storage_slots_per_account
params.storage_slots_per_account,
params.selfdestructs_per_update
),
),
&params,
Expand Down

0 comments on commit 37ef201

Please sign in to comment.