mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
perf(trie): Don't filter proofs in v2 if sparse trie as cache is enabled (#21811)
This commit is contained in:
@@ -563,7 +563,7 @@ where
|
|||||||
from_multi_proof,
|
from_multi_proof,
|
||||||
proof_worker_handle,
|
proof_worker_handle,
|
||||||
trie_metrics.clone(),
|
trie_metrics.clone(),
|
||||||
sparse_state_trie,
|
sparse_state_trie.with_skip_proof_node_filtering(true),
|
||||||
chunk_size,
|
chunk_size,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ pub struct SparseStateTrie<
|
|||||||
storage: StorageTries<S>,
|
storage: StorageTries<S>,
|
||||||
/// Flag indicating whether trie updates should be retained.
|
/// Flag indicating whether trie updates should be retained.
|
||||||
retain_updates: bool,
|
retain_updates: bool,
|
||||||
|
/// When true, skip filtering of V2 proof nodes that have already been revealed.
|
||||||
|
/// This is useful when the sparse trie is being reused across blocks and already
|
||||||
|
/// tracks revealed nodes internally.
|
||||||
|
skip_proof_node_filtering: bool,
|
||||||
/// Reusable buffer for RLP encoding of trie accounts.
|
/// Reusable buffer for RLP encoding of trie accounts.
|
||||||
account_rlp_buf: Vec<u8>,
|
account_rlp_buf: Vec<u8>,
|
||||||
/// Holds data that should be dropped after final state root is calculated.
|
/// Holds data that should be dropped after final state root is calculated.
|
||||||
@@ -67,6 +71,7 @@ where
|
|||||||
revealed_account_paths: Default::default(),
|
revealed_account_paths: Default::default(),
|
||||||
storage: Default::default(),
|
storage: Default::default(),
|
||||||
retain_updates: false,
|
retain_updates: false,
|
||||||
|
skip_proof_node_filtering: false,
|
||||||
account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
|
account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
|
||||||
deferred_drops: DeferredDrops::default(),
|
deferred_drops: DeferredDrops::default(),
|
||||||
#[cfg(feature = "metrics")]
|
#[cfg(feature = "metrics")]
|
||||||
@@ -119,6 +124,16 @@ impl<A, S> SparseStateTrie<A, S> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set whether to skip filtering of V2 proof nodes.
|
||||||
|
///
|
||||||
|
/// When true, `reveal_*_v2_proof_nodes` methods will pass all nodes directly to the
|
||||||
|
/// sparse trie without filtering already-revealed paths. This is useful when the
|
||||||
|
/// sparse trie is being reused across blocks and handles node deduplication internally.
|
||||||
|
pub const fn with_skip_proof_node_filtering(mut self, skip: bool) -> Self {
|
||||||
|
self.skip_proof_node_filtering = skip;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Takes the data structures for deferred dropping.
|
/// Takes the data structures for deferred dropping.
|
||||||
///
|
///
|
||||||
/// This allows the caller to drop the buffers later, avoiding expensive deallocations while
|
/// This allows the caller to drop the buffers later, avoiding expensive deallocations while
|
||||||
@@ -381,6 +396,7 @@ where
|
|||||||
use reth_primitives_traits::ParallelBridgeBuffered;
|
use reth_primitives_traits::ParallelBridgeBuffered;
|
||||||
|
|
||||||
let retain_updates = self.retain_updates;
|
let retain_updates = self.retain_updates;
|
||||||
|
let skip_filtering = self.skip_proof_node_filtering;
|
||||||
|
|
||||||
// Process all storage trie revealings in parallel, having first removed the
|
// Process all storage trie revealings in parallel, having first removed the
|
||||||
// `reveal_nodes` tracking and `RevealableSparseTrie`s for each account from their
|
// `reveal_nodes` tracking and `RevealableSparseTrie`s for each account from their
|
||||||
@@ -403,6 +419,7 @@ where
|
|||||||
&mut trie,
|
&mut trie,
|
||||||
&mut bufs,
|
&mut bufs,
|
||||||
retain_updates,
|
retain_updates,
|
||||||
|
skip_filtering,
|
||||||
);
|
);
|
||||||
(account, result, revealed_nodes, trie, bufs)
|
(account, result, revealed_nodes, trie, bufs)
|
||||||
})
|
})
|
||||||
@@ -493,8 +510,33 @@ where
|
|||||||
/// so no separate masks map is needed.
|
/// so no separate masks map is needed.
|
||||||
pub fn reveal_account_v2_proof_nodes(
|
pub fn reveal_account_v2_proof_nodes(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: Vec<ProofTrieNode>,
|
mut nodes: Vec<ProofTrieNode>,
|
||||||
) -> SparseStateTrieResult<()> {
|
) -> SparseStateTrieResult<()> {
|
||||||
|
if self.skip_proof_node_filtering {
|
||||||
|
let capacity = estimate_v2_proof_capacity(&nodes);
|
||||||
|
|
||||||
|
#[cfg(feature = "metrics")]
|
||||||
|
self.metrics.increment_total_account_nodes(nodes.len() as u64);
|
||||||
|
|
||||||
|
let root_node = nodes.iter().find(|n| n.path.is_empty());
|
||||||
|
let trie = if let Some(root_node) = root_node {
|
||||||
|
trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
|
||||||
|
self.state.reveal_root(
|
||||||
|
root_node.node.clone(),
|
||||||
|
root_node.masks,
|
||||||
|
self.retain_updates,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
|
||||||
|
};
|
||||||
|
trie.reserve_nodes(capacity);
|
||||||
|
trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof (unfiltered)");
|
||||||
|
trie.reveal_nodes(&mut nodes)?;
|
||||||
|
|
||||||
|
self.deferred_drops.proof_nodes_bufs.push(nodes);
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values: _metric_values } =
|
let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values: _metric_values } =
|
||||||
filter_revealed_v2_proof_nodes(nodes, &mut self.revealed_account_paths)?;
|
filter_revealed_v2_proof_nodes(nodes, &mut self.revealed_account_paths)?;
|
||||||
|
|
||||||
@@ -539,6 +581,7 @@ where
|
|||||||
trie,
|
trie,
|
||||||
&mut self.deferred_drops.proof_nodes_bufs,
|
&mut self.deferred_drops.proof_nodes_bufs,
|
||||||
self.retain_updates,
|
self.retain_updates,
|
||||||
|
self.skip_proof_node_filtering,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(feature = "metrics")]
|
#[cfg(feature = "metrics")]
|
||||||
@@ -554,12 +597,33 @@ where
|
|||||||
/// designed to handle a variety of associated public functions.
|
/// designed to handle a variety of associated public functions.
|
||||||
fn reveal_storage_v2_proof_nodes_inner(
|
fn reveal_storage_v2_proof_nodes_inner(
|
||||||
account: B256,
|
account: B256,
|
||||||
nodes: Vec<ProofTrieNode>,
|
mut nodes: Vec<ProofTrieNode>,
|
||||||
revealed_nodes: &mut HashSet<Nibbles>,
|
revealed_nodes: &mut HashSet<Nibbles>,
|
||||||
trie: &mut RevealableSparseTrie<S>,
|
trie: &mut RevealableSparseTrie<S>,
|
||||||
bufs: &mut Vec<Vec<ProofTrieNode>>,
|
bufs: &mut Vec<Vec<ProofTrieNode>>,
|
||||||
retain_updates: bool,
|
retain_updates: bool,
|
||||||
|
skip_filtering: bool,
|
||||||
) -> SparseStateTrieResult<ProofNodesMetricValues> {
|
) -> SparseStateTrieResult<ProofNodesMetricValues> {
|
||||||
|
if skip_filtering {
|
||||||
|
let capacity = estimate_v2_proof_capacity(&nodes);
|
||||||
|
let metric_values =
|
||||||
|
ProofNodesMetricValues { total_nodes: nodes.len(), skipped_nodes: 0 };
|
||||||
|
|
||||||
|
let root_node = nodes.iter().find(|n| n.path.is_empty());
|
||||||
|
let trie = if let Some(root_node) = root_node {
|
||||||
|
trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
|
||||||
|
trie.reveal_root(root_node.node.clone(), root_node.masks, retain_updates)?
|
||||||
|
} else {
|
||||||
|
trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
|
||||||
|
};
|
||||||
|
trie.reserve_nodes(capacity);
|
||||||
|
trace!(target: "trie::sparse", ?account, total_nodes = ?nodes.len(), "Revealing storage nodes from V2 proof (unfiltered)");
|
||||||
|
trie.reveal_nodes(&mut nodes)?;
|
||||||
|
|
||||||
|
bufs.push(nodes);
|
||||||
|
return Ok(metric_values)
|
||||||
|
}
|
||||||
|
|
||||||
let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values } =
|
let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values } =
|
||||||
filter_revealed_v2_proof_nodes(nodes, revealed_nodes)?;
|
filter_revealed_v2_proof_nodes(nodes, revealed_nodes)?;
|
||||||
|
|
||||||
@@ -1533,6 +1597,29 @@ struct FilteredV2ProofNodes {
|
|||||||
metric_values: ProofNodesMetricValues,
|
metric_values: ProofNodesMetricValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates capacity estimation for V2 proof nodes without filtering.
|
||||||
|
///
|
||||||
|
/// This counts nodes and their children (for branch and extension nodes) to provide
|
||||||
|
/// proper capacity hints for `reserve_nodes`. Used when `skip_proof_node_filtering` is
|
||||||
|
/// enabled and no filtering is performed.
|
||||||
|
fn estimate_v2_proof_capacity(nodes: &[ProofTrieNode]) -> usize {
|
||||||
|
let mut capacity = nodes.len();
|
||||||
|
|
||||||
|
for node in nodes {
|
||||||
|
match &node.node {
|
||||||
|
TrieNode::Branch(branch) => {
|
||||||
|
capacity += branch.state_mask.count_ones() as usize;
|
||||||
|
}
|
||||||
|
TrieNode::Extension(_) => {
|
||||||
|
capacity += 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
capacity
|
||||||
|
}
|
||||||
|
|
||||||
/// Filters V2 proof nodes that are already revealed, separates the root node if present, and
|
/// Filters V2 proof nodes that are already revealed, separates the root node if present, and
|
||||||
/// returns additional information about the number of total, skipped, and new nodes.
|
/// returns additional information about the number of total, skipped, and new nodes.
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user