Skip to content

Commit a43345b

Browse files
authored
perf(tree): only chunk multiproof targets if needed (#19326)
1 parent e894db8 commit a43345b

File tree

3 files changed

+130
-4
lines changed

3 files changed

+130
-4
lines changed

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,12 @@ impl MultiProofTask {
743743
/// Handles request for proof prefetch.
744744
///
745745
/// Returns a number of proofs that were spawned.
746-
#[instrument(level = "debug", target = "engine::tree::payload_processor::multiproof", skip_all, fields(accounts = targets.len()))]
746+
#[instrument(
747+
level = "debug",
748+
target = "engine::tree::payload_processor::multiproof",
749+
skip_all,
750+
fields(accounts = targets.len(), chunks = 0)
751+
)]
747752
fn on_prefetch_proof(&mut self, targets: MultiProofTargets) -> u64 {
748753
let proof_targets = self.get_prefetch_proof_targets(targets);
749754
self.fetched_proof_targets.extend_ref(&proof_targets);
@@ -785,10 +790,16 @@ impl MultiProofTask {
785790
chunks += 1;
786791
};
787792

788-
if should_chunk && let Some(chunk_size) = self.chunk_size {
793+
if should_chunk &&
794+
let Some(chunk_size) = self.chunk_size &&
795+
proof_targets.chunking_length() > chunk_size
796+
{
797+
let mut chunks = 0usize;
789798
for proof_targets_chunk in proof_targets.chunks(chunk_size) {
790799
dispatch(proof_targets_chunk);
800+
chunks += 1;
791801
}
802+
tracing::Span::current().record("chunks", chunks);
792803
} else {
793804
dispatch(proof_targets);
794805
}
@@ -874,7 +885,12 @@ impl MultiProofTask {
874885
/// Handles state updates.
875886
///
876887
/// Returns a number of proofs that were spawned.
877-
#[instrument(level = "debug", target = "engine::tree::payload_processor::multiproof", skip(self, update), fields(accounts = update.len()))]
888+
#[instrument(
889+
level = "debug",
890+
target = "engine::tree::payload_processor::multiproof",
891+
skip(self, update),
892+
fields(accounts = update.len(), chunks = 0)
893+
)]
878894
fn on_state_update(&mut self, source: StateChangeSource, update: EvmState) -> u64 {
879895
let hashed_state_update = evm_state_to_hashed_post_state(update);
880896

@@ -934,10 +950,16 @@ impl MultiProofTask {
934950
chunks += 1;
935951
};
936952

937-
if should_chunk && let Some(chunk_size) = self.chunk_size {
953+
if should_chunk &&
954+
let Some(chunk_size) = self.chunk_size &&
955+
not_fetched_state_update.chunking_length() > chunk_size
956+
{
957+
let mut chunks = 0usize;
938958
for chunk in not_fetched_state_update.chunks(chunk_size) {
939959
dispatch(chunk);
960+
chunks += 1;
940961
}
962+
tracing::Span::current().record("chunks", chunks);
941963
} else {
942964
dispatch(not_fetched_state_update);
943965
}

crates/trie/common/src/hashed_state.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,15 @@ impl HashedPostState {
278278
ChunkedHashedPostState::new(self, size)
279279
}
280280

281+
/// Returns the number of items that will be considered during chunking in `[Self::chunks]`.
282+
pub fn chunking_length(&self) -> usize {
283+
self.accounts.len() +
284+
self.storages
285+
.values()
286+
.map(|storage| if storage.wiped { 1 } else { 0 } + storage.storage.len())
287+
.sum::<usize>()
288+
}
289+
281290
/// Extend this hashed post state with contents of another.
282291
/// Entries in the second hashed post state take precedence.
283292
pub fn extend(&mut self, other: Self) {
@@ -1246,4 +1255,65 @@ mod tests {
12461255
assert_eq!(storage3.zero_valued_slots.len(), 1);
12471256
assert!(storage3.zero_valued_slots.contains(&B256::from([4; 32])));
12481257
}
1258+
1259+
#[test]
1260+
fn test_hashed_post_state_chunking_length() {
1261+
let addr1 = B256::from([1; 32]);
1262+
let addr2 = B256::from([2; 32]);
1263+
let addr3 = B256::from([3; 32]);
1264+
let addr4 = B256::from([4; 32]);
1265+
let slot1 = B256::from([1; 32]);
1266+
let slot2 = B256::from([2; 32]);
1267+
let slot3 = B256::from([3; 32]);
1268+
1269+
let state = HashedPostState {
1270+
accounts: B256Map::from_iter([(addr1, None), (addr2, None), (addr4, None)]),
1271+
storages: B256Map::from_iter([
1272+
(
1273+
addr1,
1274+
HashedStorage {
1275+
wiped: false,
1276+
storage: B256Map::from_iter([
1277+
(slot1, U256::ZERO),
1278+
(slot2, U256::ZERO),
1279+
(slot3, U256::ZERO),
1280+
]),
1281+
},
1282+
),
1283+
(
1284+
addr2,
1285+
HashedStorage {
1286+
wiped: true,
1287+
storage: B256Map::from_iter([
1288+
(slot1, U256::ZERO),
1289+
(slot2, U256::ZERO),
1290+
(slot3, U256::ZERO),
1291+
]),
1292+
},
1293+
),
1294+
(
1295+
addr3,
1296+
HashedStorage {
1297+
wiped: false,
1298+
storage: B256Map::from_iter([
1299+
(slot1, U256::ZERO),
1300+
(slot2, U256::ZERO),
1301+
(slot3, U256::ZERO),
1302+
]),
1303+
},
1304+
),
1305+
]),
1306+
};
1307+
1308+
let chunking_length = state.chunking_length();
1309+
for size in 1..=state.clone().chunks(1).count() {
1310+
let chunk_count = state.clone().chunks(size).count();
1311+
let expected_count = chunking_length.div_ceil(size);
1312+
assert_eq!(
1313+
chunk_count, expected_count,
1314+
"chunking_length: {}, size: {}",
1315+
chunking_length, size
1316+
);
1317+
}
1318+
}
12491319
}

crates/trie/common/src/proofs.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ impl MultiProofTargets {
8989
pub fn chunks(self, size: usize) -> ChunkedMultiProofTargets {
9090
ChunkedMultiProofTargets::new(self, size)
9191
}
92+
93+
/// Returns the number of items that will be considered during chunking in `[Self::chunks]`.
94+
pub fn chunking_length(&self) -> usize {
95+
self.values().map(|slots| 1 + slots.len().saturating_sub(1)).sum::<usize>()
96+
}
9297
}
9398

9499
/// An iterator that yields chunks of the proof targets of at most `size` account and storage
@@ -1067,4 +1072,33 @@ mod tests {
10671072
acc.storage_root = EMPTY_ROOT_HASH;
10681073
assert_eq!(acc, inverse);
10691074
}
1075+
1076+
#[test]
1077+
fn test_multiproof_targets_chunking_length() {
1078+
let mut targets = MultiProofTargets::default();
1079+
targets.insert(B256::with_last_byte(1), B256Set::default());
1080+
targets.insert(
1081+
B256::with_last_byte(2),
1082+
B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1083+
);
1084+
targets.insert(
1085+
B256::with_last_byte(3),
1086+
B256Set::from_iter([
1087+
B256::with_last_byte(30),
1088+
B256::with_last_byte(31),
1089+
B256::with_last_byte(32),
1090+
]),
1091+
);
1092+
1093+
let chunking_length = targets.chunking_length();
1094+
for size in 1..=targets.clone().chunks(1).count() {
1095+
let chunk_count = targets.clone().chunks(size).count();
1096+
let expected_count = chunking_length.div_ceil(size);
1097+
assert_eq!(
1098+
chunk_count, expected_count,
1099+
"chunking_length: {}, size: {}",
1100+
chunking_length, size
1101+
);
1102+
}
1103+
}
10701104
}

0 commit comments

Comments
 (0)