mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 09:08:05 -05:00
perf(trie): cache last hashed entry seek in trie node iter (#15471)
This commit is contained in:
@@ -72,7 +72,9 @@ impl WalkerMetrics {
|
||||
pub struct TrieNodeIterMetrics {
|
||||
/// The number of branch nodes returned by the iterator.
|
||||
branch_nodes_returned_total: Counter,
|
||||
/// The number of times same leaf node was seeked multiple times in a row by the iterator.
|
||||
/// The number of times the same hashed cursor key was seeked multiple times in a row by the
|
||||
/// iterator. It does not mean mean the database seek was actually done, as the trie node
|
||||
/// iterator caches the last hashed cursor seek.
|
||||
leaf_nodes_same_seeked_total: Counter,
|
||||
/// The number of times the same leaf node as we just advanced to was seeked by the iterator.
|
||||
leaf_nodes_same_seeked_as_advanced_total: Counter,
|
||||
|
||||
@@ -33,6 +33,17 @@ pub enum TrieElement<Value> {
|
||||
Leaf(B256, Value),
|
||||
}
|
||||
|
||||
/// Result of calling [`HashedCursor::seek`].
|
||||
#[derive(Debug)]
|
||||
struct SeekedHashedEntry<V> {
|
||||
/// The key that was seeked.
|
||||
seeked_key: B256,
|
||||
/// The result of the seek.
|
||||
|
||||
/// If no entry was found for the provided key, this will be [`None`].
|
||||
result: Option<(B256, V)>,
|
||||
}
|
||||
|
||||
/// An iterator over existing intermediate branch nodes and updated leaf nodes.
|
||||
#[derive(Debug)]
|
||||
pub struct TrieNodeIter<C, H: HashedCursor> {
|
||||
@@ -45,19 +56,26 @@ pub struct TrieNodeIter<C, H: HashedCursor> {
|
||||
previous_hashed_key: Option<B256>,
|
||||
|
||||
/// Current hashed entry.
|
||||
current_hashed_entry: Option<(B256, <H as HashedCursor>::Value)>,
|
||||
current_hashed_entry: Option<(B256, H::Value)>,
|
||||
/// Flag indicating whether we should check the current walker key.
|
||||
should_check_walker_key: bool,
|
||||
|
||||
/// The last seeked hashed entry.
|
||||
///
|
||||
/// We use it to not seek the same hashed entry twice, and instead re-use it.
|
||||
last_seeked_hashed_entry: Option<SeekedHashedEntry<H::Value>>,
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
metrics: crate::metrics::TrieNodeIterMetrics,
|
||||
#[cfg(feature = "metrics")]
|
||||
previously_seeked_key: Option<B256>,
|
||||
/// The key that the [`HashedCursor`] previously advanced to using [`HashedCursor::next`].
|
||||
#[cfg(feature = "metrics")]
|
||||
previously_advanced_to_key: Option<B256>,
|
||||
}
|
||||
|
||||
impl<C, H: HashedCursor> TrieNodeIter<C, H> {
|
||||
impl<C, H: HashedCursor> TrieNodeIter<C, H>
|
||||
where
|
||||
H::Value: Copy,
|
||||
{
|
||||
/// Creates a new [`TrieNodeIter`].
|
||||
pub fn new(walker: TrieWalker<C>, hashed_cursor: H, trie_type: TrieType) -> Self {
|
||||
Self {
|
||||
@@ -66,11 +84,10 @@ impl<C, H: HashedCursor> TrieNodeIter<C, H> {
|
||||
previous_hashed_key: None,
|
||||
current_hashed_entry: None,
|
||||
should_check_walker_key: false,
|
||||
last_seeked_hashed_entry: None,
|
||||
#[cfg(feature = "metrics")]
|
||||
metrics: crate::metrics::TrieNodeIterMetrics::new(trie_type),
|
||||
#[cfg(feature = "metrics")]
|
||||
previously_seeked_key: None,
|
||||
#[cfg(feature = "metrics")]
|
||||
previously_advanced_to_key: None,
|
||||
}
|
||||
}
|
||||
@@ -84,28 +101,39 @@ impl<C, H: HashedCursor> TrieNodeIter<C, H> {
|
||||
|
||||
/// Seeks the hashed cursor to the given key.
|
||||
///
|
||||
/// If the key is the same as the last seeked key, the result of the last seek is returned.
|
||||
///
|
||||
/// If `metrics` feature is enabled, also updates the metrics.
|
||||
fn hashed_cursor_seek(&mut self, key: B256) -> Result<Option<(B256, H::Value)>, DatabaseError> {
|
||||
fn seek_hashed_entry(&mut self, key: B256) -> Result<Option<(B256, H::Value)>, DatabaseError> {
|
||||
if let Some(entry) = self
|
||||
.last_seeked_hashed_entry
|
||||
.as_ref()
|
||||
.filter(|entry| entry.seeked_key == key)
|
||||
.map(|entry| entry.result)
|
||||
{
|
||||
#[cfg(feature = "metrics")]
|
||||
self.metrics.inc_leaf_nodes_same_seeked();
|
||||
return Ok(entry);
|
||||
}
|
||||
|
||||
let result = self.hashed_cursor.seek(key)?;
|
||||
self.last_seeked_hashed_entry = Some(SeekedHashedEntry { seeked_key: key, result });
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
{
|
||||
self.metrics.inc_leaf_nodes_seeked();
|
||||
|
||||
if Some(key) == self.previously_seeked_key {
|
||||
self.metrics.inc_leaf_nodes_same_seeked();
|
||||
}
|
||||
self.previously_seeked_key = Some(key);
|
||||
|
||||
if Some(key) == self.previously_advanced_to_key {
|
||||
self.metrics.inc_leaf_nodes_same_seeked_as_advanced();
|
||||
}
|
||||
}
|
||||
self.hashed_cursor.seek(key)
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Advances the hashed cursor to the next entry.
|
||||
///
|
||||
/// If `metrics` feature is enabled, also updates the metrics.
|
||||
fn hashed_cursor_next(&mut self) -> Result<Option<(B256, H::Value)>, DatabaseError> {
|
||||
fn next_hashed_entry(&mut self) -> Result<Option<(B256, H::Value)>, DatabaseError> {
|
||||
let result = self.hashed_cursor.next();
|
||||
#[cfg(feature = "metrics")]
|
||||
{
|
||||
@@ -122,6 +150,7 @@ impl<C, H> TrieNodeIter<C, H>
|
||||
where
|
||||
C: TrieCursor,
|
||||
H: HashedCursor,
|
||||
H::Value: Copy,
|
||||
{
|
||||
/// Return the next trie node to be added to the hash builder.
|
||||
///
|
||||
@@ -169,7 +198,7 @@ where
|
||||
|
||||
// Set the next hashed entry as a leaf node and return
|
||||
trace!(target: "trie::node_iter", ?hashed_key, "next hashed entry");
|
||||
self.current_hashed_entry = self.hashed_cursor_next()?;
|
||||
self.current_hashed_entry = self.next_hashed_entry()?;
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
self.metrics.inc_leaf_nodes_returned();
|
||||
@@ -181,8 +210,8 @@ where
|
||||
Some(hashed_key) => {
|
||||
trace!(target: "trie::node_iter", ?hashed_key, "seeking to the previous hashed entry");
|
||||
// Seek to the previous hashed key and get the next hashed entry
|
||||
self.hashed_cursor_seek(hashed_key)?;
|
||||
self.current_hashed_entry = self.hashed_cursor_next()?;
|
||||
self.seek_hashed_entry(hashed_key)?;
|
||||
self.current_hashed_entry = self.next_hashed_entry()?;
|
||||
}
|
||||
None => {
|
||||
// Get the seek key and set the current hashed entry based on walker's next
|
||||
@@ -232,7 +261,7 @@ where
|
||||
continue
|
||||
}
|
||||
|
||||
self.current_hashed_entry = self.hashed_cursor_seek(seek_key)?;
|
||||
self.current_hashed_entry = self.seek_hashed_entry(seek_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -470,11 +499,6 @@ mod tests {
|
||||
visit_type: KeyVisitType::SeekNonExact(account_3),
|
||||
visited_key: Some(account_3)
|
||||
},
|
||||
// Why do we seek the account 3 one more time?
|
||||
KeyVisit {
|
||||
visit_type: KeyVisitType::SeekNonExact(account_3),
|
||||
visited_key: Some(account_3)
|
||||
},
|
||||
// Collect the siblings of the modified account
|
||||
KeyVisit { visit_type: KeyVisitType::Next, visited_key: Some(account_4) },
|
||||
KeyVisit { visit_type: KeyVisitType::Next, visited_key: Some(account_5) },
|
||||
|
||||
Reference in New Issue
Block a user