Skip to content

Commit 3e0960c

Browse files
Rjectedshekhiringraphite-app[bot]
authored
perf: reuse accounts trie in payload processing (#16181)
Co-authored-by: Alexey Shekhirin <[email protected]> Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
1 parent a8522e6 commit 3e0960c

File tree

6 files changed

+221
-36
lines changed

6 files changed

+221
-36
lines changed

crates/engine/tree/benches/state_root_task.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ fn bench_state_root(c: &mut Criterion) {
227227

228228
(genesis_hash, payload_processor, provider, state_updates)
229229
},
230-
|(genesis_hash, payload_processor, provider, state_updates)| {
230+
|(genesis_hash, mut payload_processor, provider, state_updates)| {
231231
black_box({
232232
let mut handle = payload_processor.spawn(
233233
Default::default(),

crates/engine/tree/src/tree/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2283,7 +2283,7 @@ where
22832283
// background task or try to compute it in parallel
22842284
if use_state_root_task {
22852285
match handle.state_root() {
2286-
Ok(StateRootComputeOutcome { state_root, trie_updates }) => {
2286+
Ok(StateRootComputeOutcome { state_root, trie_updates, trie }) => {
22872287
let elapsed = execution_finish.elapsed();
22882288
info!(target: "engine::tree", ?state_root, ?elapsed, "State root task finished");
22892289
// we double check the state root here for good measure
@@ -2297,6 +2297,9 @@ where
22972297
"State root task returned incorrect state root"
22982298
);
22992299
}
2300+
2301+
// hold on to the sparse trie for the next payload
2302+
self.payload_processor.set_sparse_trie(trie);
23002303
}
23012304
Err(error) => {
23022305
debug!(target: "engine::tree", %error, "Background parallel state root computation failed");

crates/engine/tree/src/tree/payload_processor/mod.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use reth_trie_parallel::{
2828
proof_task::{ProofTaskCtx, ProofTaskManager},
2929
root::ParallelStateRootError,
3030
};
31+
use reth_trie_sparse::SparseTrieState;
3132
use std::{
3233
collections::VecDeque,
3334
sync::{
@@ -67,6 +68,9 @@ where
6768
precompile_cache_disabled: bool,
6869
/// Precompile cache map.
6970
precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
71+
/// A sparse trie, kept around to be used for the state root computation so that allocations
72+
/// can be minimized.
73+
sparse_trie: Option<SparseTrieState>,
7074
_marker: std::marker::PhantomData<N>,
7175
}
7276

@@ -91,6 +95,7 @@ where
9195
evm_config,
9296
precompile_cache_disabled: config.precompile_cache_disabled(),
9397
precompile_cache_map,
98+
sparse_trie: None,
9499
_marker: Default::default(),
95100
}
96101
}
@@ -134,7 +139,7 @@ where
134139
/// This returns a handle to await the final state root and to interact with the tasks (e.g.
135140
/// canceling)
136141
pub fn spawn<P>(
137-
&self,
142+
&mut self,
138143
header: SealedHeaderFor<N>,
139144
transactions: VecDeque<Recovered<N::SignedTx>>,
140145
provider_builder: StateProviderBuilder<N, P>,
@@ -191,11 +196,15 @@ where
191196
multi_proof_task.run();
192197
});
193198

194-
let mut sparse_trie_task = SparseTrieTask::new(
199+
// take the sparse trie if it was set
200+
let sparse_trie = self.sparse_trie.take();
201+
202+
let mut sparse_trie_task = SparseTrieTask::new_with_stored_trie(
195203
self.executor.clone(),
196204
sparse_trie_rx,
197205
proof_task.handle(),
198206
self.trie_metrics.clone(),
207+
sparse_trie,
199208
);
200209

201210
// wire the sparse trie to the state root response receiver
@@ -241,6 +250,11 @@ where
241250
PayloadHandle { to_multi_proof: None, prewarm_handle, state_root: None }
242251
}
243252

253+
/// Sets the sparse trie to be kept around for the state root computation.
254+
pub(super) fn set_sparse_trie(&mut self, sparse_trie: SparseTrieState) {
255+
self.sparse_trie = Some(sparse_trie);
256+
}
257+
244258
/// Spawn prewarming optionally wired to the multiproof task for target updates.
245259
fn spawn_caching_with<P>(
246260
&self,
@@ -566,7 +580,7 @@ mod tests {
566580
}
567581
}
568582

569-
let payload_processor = PayloadProcessor::<EthPrimitives, _>::new(
583+
let mut payload_processor = PayloadProcessor::<EthPrimitives, _>::new(
570584
WorkloadExecutor::default(),
571585
EthEvmConfig::new(factory.chain_spec()),
572586
&TreeConfig::default(),

crates/engine/tree/src/tree/payload_processor/sparse_trie.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use reth_trie_parallel::root::ParallelStateRootError;
1111
use reth_trie_sparse::{
1212
blinded::{BlindedProvider, BlindedProviderFactory},
1313
errors::{SparseStateTrieResult, SparseTrieErrorKind},
14-
SparseStateTrie,
14+
SparseStateTrie, SparseTrieState,
1515
};
1616
use std::{
1717
sync::mpsc,
@@ -63,6 +63,43 @@ where
6363
}
6464
}
6565

66+
/// Creates a new sparse trie, populating the accounts trie with the given cleared
67+
/// `SparseTrieState` if it exists.
68+
pub(super) fn new_with_stored_trie(
69+
executor: WorkloadExecutor,
70+
updates: mpsc::Receiver<SparseTrieUpdate>,
71+
blinded_provider_factory: BPF,
72+
trie_metrics: MultiProofTaskMetrics,
73+
sparse_trie_state: Option<SparseTrieState>,
74+
) -> Self {
75+
if let Some(sparse_trie_state) = sparse_trie_state {
76+
Self::with_accounts_trie(
77+
executor,
78+
updates,
79+
blinded_provider_factory,
80+
trie_metrics,
81+
sparse_trie_state,
82+
)
83+
} else {
84+
Self::new(executor, updates, blinded_provider_factory, trie_metrics)
85+
}
86+
}
87+
88+
/// Creates a new sparse trie task, using the given cleared `SparseTrieState` for the accounts
89+
/// trie.
90+
pub(super) fn with_accounts_trie(
91+
executor: WorkloadExecutor,
92+
updates: mpsc::Receiver<SparseTrieUpdate>,
93+
blinded_provider_factory: BPF,
94+
metrics: MultiProofTaskMetrics,
95+
sparse_trie_state: SparseTrieState,
96+
) -> Self {
97+
let mut trie = SparseStateTrie::new(blinded_provider_factory).with_updates(true);
98+
trie.populate_from(sparse_trie_state);
99+
100+
Self { executor, updates, metrics, trie }
101+
}
102+
66103
/// Runs the sparse trie task to completion.
67104
///
68105
/// This waits for new incoming [`SparseTrieUpdate`].
@@ -109,7 +146,10 @@ where
109146
self.metrics.sparse_trie_final_update_duration_histogram.record(start.elapsed());
110147
self.metrics.sparse_trie_total_duration_histogram.record(now.elapsed());
111148

112-
Ok(StateRootComputeOutcome { state_root, trie_updates })
149+
// take the account trie
150+
let trie = self.trie.take_cleared_account_trie_state();
151+
152+
Ok(StateRootComputeOutcome { state_root, trie_updates, trie })
113153
}
114154
}
115155

@@ -121,6 +161,8 @@ pub struct StateRootComputeOutcome {
121161
pub state_root: B256,
122162
/// The trie updates.
123163
pub trie_updates: TrieUpdates,
164+
/// The account state trie.
165+
pub trie: SparseTrieState,
124166
}
125167

126168
/// Updates the sparse trie with the given proofs and state, and returns the elapsed time.

crates/trie/sparse/src/state.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
blinded::{BlindedProvider, BlindedProviderFactory, DefaultBlindedProviderFactory},
3-
LeafLookup, RevealedSparseTrie, SparseTrie, TrieMasks,
3+
LeafLookup, RevealedSparseTrie, SparseTrie, SparseTrieState, TrieMasks,
44
};
55
use alloc::{collections::VecDeque, vec::Vec};
66
use alloy_primitives::{
@@ -107,6 +107,19 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
107107
self.revealed_account_paths.contains(&Nibbles::unpack(account))
108108
}
109109

110+
/// Uses the input `SparseTrieState` to populate the backing data structures in the `state`
111+
/// trie.
112+
pub fn populate_from(&mut self, trie: SparseTrieState) {
113+
if let Some(new_trie) = self.state.as_revealed_mut() {
114+
new_trie.use_allocated_state(trie);
115+
} else {
116+
self.state = SparseTrie::revealed_with_provider(
117+
self.provider_factory.account_node_provider(),
118+
trie,
119+
)
120+
}
121+
}
122+
110123
/// Was the account witness for `address` complete?
111124
pub fn check_valid_account_witness(&self, address: B256) -> bool {
112125
let path = Nibbles::unpack(address);
@@ -343,7 +356,7 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
343356
) -> SparseStateTrieResult<()> {
344357
let FilteredProofNodes {
345358
nodes,
346-
new_nodes,
359+
new_nodes: _,
347360
total_nodes: _total_nodes,
348361
skipped_nodes: _skipped_nodes,
349362
} = filter_revealed_nodes(account_subtree, &self.revealed_account_paths)?;
@@ -366,9 +379,6 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
366379
self.retain_updates,
367380
)?;
368381

369-
// Reserve the capacity for new nodes ahead of time.
370-
trie.reserve_nodes(new_nodes);
371-
372382
// Reveal the remaining proof nodes.
373383
for (path, node) in account_nodes {
374384
let (hash_mask, tree_mask) = if let TrieNode::Branch(_) = node {
@@ -650,7 +660,7 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
650660
&mut self,
651661
) -> SparseStateTrieResult<&mut RevealedSparseTrie<F::AccountNodeProvider>> {
652662
match self.state {
653-
SparseTrie::Blind => {
663+
SparseTrie::Blind | SparseTrie::AllocatedEmpty { .. } => {
654664
let (root_node, hash_mask, tree_mask) = self
655665
.provider_factory
656666
.account_node_provider()
@@ -868,6 +878,12 @@ impl<F: BlindedProviderFactory> SparseStateTrie<F> {
868878
storage_trie.remove_leaf(slot)?;
869879
Ok(())
870880
}
881+
882+
/// Clears and takes the account trie.
883+
pub fn take_cleared_account_trie_state(&mut self) -> SparseTrieState {
884+
let trie = core::mem::take(&mut self.state);
885+
trie.cleared()
886+
}
871887
}
872888

873889
/// Result of [`filter_revealed_nodes`].

0 commit comments

Comments
 (0)