Skip to content

Commit c4a2313

Browse files
authored
perf(engine): lazy load overlay trie state (#10541)
1 parent 1ac5336 commit c4a2313

File tree

3 files changed

+52
-34
lines changed

3 files changed

+52
-34
lines changed

crates/chain-state/src/in_memory.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ impl CanonicalInMemoryState {
493493
Vec::new()
494494
};
495495

496-
MemoryOverlayStateProvider::new(in_memory, historical)
496+
MemoryOverlayStateProvider::new(historical, in_memory)
497497
}
498498

499499
/// Returns an iterator over all canonical blocks in the in-memory state, from newest to oldest.

crates/chain-state/src/memory_overlay.rs

+50-32
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,18 @@ use reth_trie::{
1111
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
1212
HashedStorage,
1313
};
14-
use std::collections::HashMap;
14+
use std::{collections::HashMap, sync::OnceLock};
1515

1616
/// A state provider that stores references to in-memory blocks along with their state as well as
1717
/// the historical state provider for fallback lookups.
1818
#[allow(missing_debug_implementations)]
1919
pub struct MemoryOverlayStateProvider {
20-
/// The collection of executed parent blocks. Expected order is newest to oldest.
21-
pub(crate) in_memory: Vec<ExecutedBlock>,
22-
/// The collection of hashed state from in-memory blocks.
23-
pub(crate) hashed_post_state: HashedPostState,
24-
/// The collection of aggregated in-memory trie updates.
25-
pub(crate) trie_updates: TrieUpdates,
2620
/// Historical state provider for state lookups that are not found in in-memory blocks.
2721
pub(crate) historical: Box<dyn StateProvider>,
22+
/// The collection of executed parent blocks. Expected order is newest to oldest.
23+
pub(crate) in_memory: Vec<ExecutedBlock>,
24+
/// Lazy-loaded in-memory trie data.
25+
pub(crate) trie_state: OnceLock<MemoryOverlayTrieState>,
2826
}
2927

3028
impl MemoryOverlayStateProvider {
@@ -35,20 +33,29 @@ impl MemoryOverlayStateProvider {
3533
/// - `in_memory` - the collection of executed ancestor blocks in reverse.
3634
/// - `historical` - a historical state provider for the latest ancestor block stored in the
3735
/// database.
38-
pub fn new(in_memory: Vec<ExecutedBlock>, historical: Box<dyn StateProvider>) -> Self {
39-
let mut hashed_post_state = HashedPostState::default();
40-
let mut trie_updates = TrieUpdates::default();
41-
for block in in_memory.iter().rev() {
42-
hashed_post_state.extend_ref(block.hashed_state.as_ref());
43-
trie_updates.extend_ref(block.trie.as_ref());
44-
}
45-
Self { in_memory, hashed_post_state, trie_updates, historical }
36+
pub fn new(historical: Box<dyn StateProvider>, in_memory: Vec<ExecutedBlock>) -> Self {
37+
Self { historical, in_memory, trie_state: OnceLock::new() }
4638
}
4739

4840
/// Turn this state provider into a [`StateProviderBox`]
4941
pub fn boxed(self) -> StateProviderBox {
5042
Box::new(self)
5143
}
44+
45+
/// Return lazy-loaded trie state aggregated from in-memory blocks.
46+
fn trie_state(&self) -> MemoryOverlayTrieState {
47+
self.trie_state
48+
.get_or_init(|| {
49+
let mut hashed_state = HashedPostState::default();
50+
let mut trie_nodes = TrieUpdates::default();
51+
for block in self.in_memory.iter().rev() {
52+
hashed_state.extend_ref(block.hashed_state.as_ref());
53+
trie_nodes.extend_ref(block.trie.as_ref());
54+
}
55+
MemoryOverlayTrieState { trie_nodes, hashed_state }
56+
})
57+
.clone()
58+
}
5259
}
5360

5461
impl BlockHashReader for MemoryOverlayStateProvider {
@@ -105,14 +112,13 @@ impl StateRootProvider for MemoryOverlayStateProvider {
105112
fn hashed_state_root_from_nodes(
106113
&self,
107114
nodes: TrieUpdates,
108-
hashed_state: HashedPostState,
115+
state: HashedPostState,
109116
prefix_sets: TriePrefixSetsMut,
110117
) -> ProviderResult<B256> {
111-
let mut trie_nodes = self.trie_updates.clone();
118+
let MemoryOverlayTrieState { mut trie_nodes, mut hashed_state } = self.trie_state();
112119
trie_nodes.extend(nodes);
113-
let mut state = self.hashed_post_state.clone();
114-
state.extend(hashed_state);
115-
self.historical.hashed_state_root_from_nodes(trie_nodes, state, prefix_sets)
120+
hashed_state.extend(state);
121+
self.historical.hashed_state_root_from_nodes(trie_nodes, hashed_state, prefix_sets)
116122
}
117123

118124
fn hashed_state_root_with_updates(
@@ -130,14 +136,17 @@ impl StateRootProvider for MemoryOverlayStateProvider {
130136
fn hashed_state_root_from_nodes_with_updates(
131137
&self,
132138
nodes: TrieUpdates,
133-
hashed_state: HashedPostState,
139+
state: HashedPostState,
134140
prefix_sets: TriePrefixSetsMut,
135141
) -> ProviderResult<(B256, TrieUpdates)> {
136-
let mut trie_nodes = self.trie_updates.clone();
142+
let MemoryOverlayTrieState { mut trie_nodes, mut hashed_state } = self.trie_state();
137143
trie_nodes.extend(nodes);
138-
let mut state = self.hashed_post_state.clone();
139-
state.extend(hashed_state);
140-
self.historical.hashed_state_root_from_nodes_with_updates(trie_nodes, state, prefix_sets)
144+
hashed_state.extend(state);
145+
self.historical.hashed_state_root_from_nodes_with_updates(
146+
trie_nodes,
147+
hashed_state,
148+
prefix_sets,
149+
)
141150
}
142151

143152
// TODO: Currently this does not reuse available in-memory trie nodes.
@@ -153,13 +162,13 @@ impl StateRootProvider for MemoryOverlayStateProvider {
153162
impl StateProofProvider for MemoryOverlayStateProvider {
154163
fn hashed_proof(
155164
&self,
156-
hashed_state: HashedPostState,
165+
state: HashedPostState,
157166
address: Address,
158167
slots: &[B256],
159168
) -> ProviderResult<AccountProof> {
160-
let mut state = self.hashed_post_state.clone();
161-
state.extend(hashed_state);
162-
self.historical.hashed_proof(state, address, slots)
169+
let MemoryOverlayTrieState { mut hashed_state, .. } = self.trie_state();
170+
hashed_state.extend(state);
171+
self.historical.hashed_proof(hashed_state, address, slots)
163172
}
164173

165174
// TODO: Currently this does not reuse available in-memory trie nodes.
@@ -168,9 +177,9 @@ impl StateProofProvider for MemoryOverlayStateProvider {
168177
overlay: HashedPostState,
169178
target: HashedPostState,
170179
) -> ProviderResult<HashMap<B256, Bytes>> {
171-
let mut state = self.hashed_post_state.clone();
172-
state.extend(overlay);
173-
self.historical.witness(state, target)
180+
let MemoryOverlayTrieState { mut hashed_state, .. } = self.trie_state();
181+
hashed_state.extend(overlay);
182+
self.historical.witness(hashed_state, target)
174183
}
175184
}
176185

@@ -199,3 +208,12 @@ impl StateProvider for MemoryOverlayStateProvider {
199208
self.historical.bytecode_by_hash(code_hash)
200209
}
201210
}
211+
212+
/// The collection of data necessary for trie-related operations for [`MemoryOverlayStateProvider`].
213+
#[derive(Clone, Debug)]
214+
pub(crate) struct MemoryOverlayTrieState {
215+
/// The collection of aggregated in-memory trie updates.
216+
pub(crate) trie_nodes: TrieUpdates,
217+
/// The collection of hashed state from in-memory blocks.
218+
pub(crate) hashed_state: HashedPostState,
219+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ where
12321232
trace!(target: "engine", %hash, "found canonical state for block in memory");
12331233
// the block leads back to the canonical chain
12341234
let historical = self.provider.state_by_block_hash(historical)?;
1235-
return Ok(Some(Box::new(MemoryOverlayStateProvider::new(blocks, historical))))
1235+
return Ok(Some(Box::new(MemoryOverlayStateProvider::new(historical, blocks))))
12361236
}
12371237

12381238
// the hash could belong to an unknown block or a persisted block

0 commit comments

Comments
 (0)