perf(trie): use TrieMask iterator for efficient bit iteration (#21676)

This commit is contained in:
DaniPopes
2026-02-03 20:23:41 +01:00
committed by GitHub
parent bc729671d9
commit e6fc5ff54b
6 changed files with 70 additions and 85 deletions

33
Cargo.lock generated
View File

@@ -938,9 +938,9 @@ dependencies = [
[[package]]
name = "alloy-trie"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428aa0f0e0658ff091f8f667c406e034b431cb10abd39de4f507520968acc499"
checksum = "4d7fd448ab0a017de542de1dcca7a58e7019fe0e7a34ed3f9543ebddf6aceffa"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@@ -950,9 +950,10 @@ dependencies = [
"derive_more",
"nybbles",
"proptest",
"proptest-derive 0.5.1",
"proptest-derive 0.7.0",
"serde",
"smallvec",
"thiserror 2.0.18",
"tracing",
]
@@ -1019,7 +1020,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -1030,7 +1031,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -3111,7 +3112,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -3441,7 +3442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -5182,7 +5183,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -6146,7 +6147,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -7080,9 +7081,9 @@ dependencies = [
[[package]]
name = "proptest-derive"
version = "0.5.1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49"
checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30"
dependencies = [
"proc-macro2",
"quote",
@@ -7091,9 +7092,9 @@ dependencies = [
[[package]]
name = "proptest-derive"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30"
checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c"
dependencies = [
"proc-macro2",
"quote",
@@ -11787,7 +11788,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -12647,7 +12648,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -13891,7 +13892,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]

View File

@@ -492,7 +492,7 @@ alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-eip7928 = { version = "0.3.0", default-features = false }
alloy-evm = { version = "0.27.2", default-features = false }
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
alloy-trie = { version = "0.9.1", default-features = false }
alloy-trie = { version = "0.9.4", default-features = false }
alloy-hardforks = "0.4.5"

View File

@@ -85,4 +85,6 @@ pub mod serde_bincode_compat {
}
/// Re-export
pub use alloy_trie::{nodes::*, proof, BranchNodeCompact, HashBuilder, TrieMask, EMPTY_ROOT_HASH};
pub use alloy_trie::{
nodes::*, proof, BranchNodeCompact, HashBuilder, TrieMask, TrieMaskIter, EMPTY_ROOT_HASH,
};

View File

@@ -10,7 +10,7 @@ use reth_execution_errors::{SparseTrieError, SparseTrieErrorKind, SparseTrieResu
use reth_trie_common::{
prefix_set::{PrefixSet, PrefixSetMut},
BranchNodeMasks, BranchNodeMasksMap, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles,
ProofTrieNode, RlpNode, TrieNode, CHILD_INDEX_RANGE,
ProofTrieNode, RlpNode, TrieNode,
};
use reth_trie_sparse::{
provider::{RevealedNode, TrieNodeProvider},
@@ -2087,15 +2087,13 @@ impl ParallelSparseTrie {
// in the lower subtrie, and reveal accordingly.
if !SparseSubtrieType::path_len_is_upper(path.len() + 1) {
let mut stack_ptr = branch.as_ref().first_child_index();
for idx in CHILD_INDEX_RANGE {
if branch.state_mask.is_bit_set(idx) {
let mut child_path = path;
child_path.push_unchecked(idx);
self.lower_subtrie_for_path_mut(&child_path)
.expect("child_path must have a lower subtrie")
.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
stack_ptr += 1;
}
for idx in branch.state_mask.iter() {
let mut child_path = path;
child_path.push_unchecked(idx);
self.lower_subtrie_for_path_mut(&child_path)
.expect("child_path must have a lower subtrie")
.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
stack_ptr += 1;
}
}
}
@@ -2629,19 +2627,17 @@ impl SparseSubtrie {
self.nodes.insert(path, SparseNode::Empty);
}
TrieNode::Branch(branch) => {
// For a branch node, iterate over all potential children
// For a branch node, iterate over all children
let mut stack_ptr = branch.as_ref().first_child_index();
for idx in CHILD_INDEX_RANGE {
if branch.state_mask.is_bit_set(idx) {
let mut child_path = path;
child_path.push_unchecked(idx);
if Self::is_child_same_level(&path, &child_path) {
// Reveal each child node or hash it has, but only if the child is on
// the same level as the parent.
self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
}
stack_ptr += 1;
for idx in branch.state_mask.iter() {
let mut child_path = path;
child_path.push_unchecked(idx);
if Self::is_child_same_level(&path, &child_path) {
// Reveal each child node or hash it has, but only if the child is on
// the same level as the parent.
self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
}
stack_ptr += 1;
}
// Update the branch node entry in the nodes map, handling cases where a blinded
// node is now replaced with a revealed node.
@@ -3071,12 +3067,10 @@ impl SparseSubtrieInner {
self.buffers.branch_child_buf.clear();
// Walk children in a reverse order from `f` to `0`, so we pop the `0` first
// from the stack and keep walking in the sorted order.
for bit in CHILD_INDEX_RANGE.rev() {
if state_mask.is_bit_set(bit) {
let mut child = path;
child.push_unchecked(bit);
self.buffers.branch_child_buf.push(child);
}
for bit in state_mask.iter().rev() {
let mut child = path;
child.push_unchecked(bit);
self.buffers.branch_child_buf.push(child);
}
self.buffers

View File

@@ -21,8 +21,7 @@ use reth_execution_errors::{SparseTrieErrorKind, SparseTrieResult};
use reth_trie_common::{
prefix_set::{PrefixSet, PrefixSetMut},
BranchNodeCompact, BranchNodeMasks, BranchNodeMasksMap, BranchNodeRef, ExtensionNodeRef,
LeafNodeRef, Nibbles, ProofTrieNode, RlpNode, TrieMask, TrieNode, CHILD_INDEX_RANGE,
EMPTY_ROOT_HASH,
LeafNodeRef, Nibbles, ProofTrieNode, RlpNode, TrieMask, TrieNode, EMPTY_ROOT_HASH,
};
use tracing::{debug, instrument, trace};
@@ -422,13 +421,11 @@ impl fmt::Display for SerialSparseTrie {
SparseNode::Branch { state_mask, .. } => {
writeln!(f, "{packed_path} -> {node:?}")?;
for i in CHILD_INDEX_RANGE.rev() {
if state_mask.is_bit_set(i) {
let mut child_path = path;
child_path.push_unchecked(i);
if let Some(child_node) = self.nodes_ref().get(&child_path) {
stack.push((child_path, child_node, depth + 1));
}
for i in state_mask.iter().rev() {
let mut child_path = path;
child_path.push_unchecked(i);
if let Some(child_node) = self.nodes_ref().get(&child_path) {
stack.push((child_path, child_node, depth + 1));
}
}
}
@@ -503,16 +500,14 @@ impl SparseTrieTrait for SerialSparseTrie {
self.nodes.insert(path, SparseNode::Empty);
}
TrieNode::Branch(branch) => {
// For a branch node, iterate over all potential children
// For a branch node, iterate over all children
let mut stack_ptr = branch.as_ref().first_child_index();
for idx in CHILD_INDEX_RANGE {
if branch.state_mask.is_bit_set(idx) {
let mut child_path = path;
child_path.push_unchecked(idx);
// Reveal each child node or hash it has
self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
stack_ptr += 1;
}
for idx in branch.state_mask.iter() {
let mut child_path = path;
child_path.push_unchecked(idx);
// Reveal each child node or hash it has
self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
stack_ptr += 1;
}
// Update the branch node entry in the nodes map, handling cases where a blinded
// node is now replaced with a revealed node.
@@ -1500,12 +1495,10 @@ impl SerialSparseTrie {
} else {
unchanged_prefix_set.insert(path);
for bit in CHILD_INDEX_RANGE.rev() {
if state_mask.is_bit_set(bit) {
let mut child_path = path;
child_path.push_unchecked(bit);
paths.push((child_path, level + 1));
}
for bit in state_mask.iter().rev() {
let mut child_path = path;
child_path.push_unchecked(bit);
paths.push((child_path, level + 1));
}
}
}
@@ -1659,12 +1652,10 @@ impl SerialSparseTrie {
buffers.branch_child_buf.clear();
// Walk children in a reverse order from `f` to `0`, so we pop the `0` first
// from the stack and keep walking in the sorted order.
for bit in CHILD_INDEX_RANGE.rev() {
if state_mask.is_bit_set(bit) {
let mut child = path;
child.push_unchecked(bit);
buffers.branch_child_buf.push(child);
}
for bit in state_mask.iter().rev() {
let mut child = path;
child.push_unchecked(bit);
buffers.branch_child_buf.push(child);
}
buffers

View File

@@ -1,4 +1,4 @@
use crate::{BranchNodeCompact, Nibbles, StoredSubNode, CHILD_INDEX_RANGE};
use crate::{BranchNodeCompact, Nibbles, StoredSubNode};
use alloy_primitives::B256;
use alloy_trie::proof::AddedRemovedKeys;
@@ -57,15 +57,12 @@ impl CursorSubNode {
/// Creates a new [`CursorSubNode`] from a key and an optional node.
pub fn new(key: Nibbles, node: Option<BranchNodeCompact>) -> Self {
// Find the first nibble that is set in the state mask of the node.
let position = node.as_ref().filter(|n| n.root_hash.is_none()).map_or(
SubNodePosition::ParentBranch,
|n| {
let mut child_index_range = CHILD_INDEX_RANGE;
SubNodePosition::Child(
child_index_range.find(|i| n.state_mask.is_bit_set(*i)).unwrap(),
)
},
);
let position = node
.as_ref()
.filter(|n| n.root_hash.is_none())
.map_or(SubNodePosition::ParentBranch, |n| {
SubNodePosition::Child(n.state_mask.iter().next().unwrap())
});
Self::new_with_full_key(key, node, position)
}