feat(trie): add metrics for sparse trie cache retained memory (#22697)

Co-authored-by: Sergei Shulepov <2205845+pepyakin@users.noreply.github.com>
This commit is contained in:
Derek Cofausper
2026-03-02 03:41:45 -08:00
committed by GitHub
parent 75ca930237
commit 35fc3b684f
5 changed files with 65 additions and 0 deletions

View File

@@ -653,6 +653,12 @@ where
trie_metrics
.into_trie_for_reuse_duration_histogram
.record(start.elapsed().as_secs_f64());
trie_metrics
.sparse_trie_retained_memory_bytes
.set(trie.memory_size() as f64);
trie_metrics
.sparse_trie_retained_storage_tries
.set(trie.retained_storage_tries_count() as f64);
guard.store(PreservedSparseTrie::anchored(trie, result.state_root));
deferred
} else {

View File

@@ -190,6 +190,11 @@ pub(crate) struct MultiProofTaskMetrics {
pub into_trie_for_reuse_duration_histogram: Histogram,
/// Time spent waiting for preserved sparse trie cache to become available.
pub sparse_trie_cache_wait_duration_histogram: Histogram,
/// Retained memory of the preserved sparse trie cache in bytes.
pub sparse_trie_retained_memory_bytes: Gauge,
/// Number of storage tries retained in the preserved sparse trie cache.
pub sparse_trie_retained_storage_tries: Gauge,
}
/// Dispatches work items as a single unit or in chunks based on target size and worker

View File

@@ -1153,6 +1153,10 @@ impl SparseTrie for ParallelSparseTrie {
upper_count + lower_count
}
fn memory_size(&self) -> usize {
self.memory_size()
}
fn prune(&mut self, max_depth: usize) -> usize {
#[cfg(feature = "trie-debug")]
self.debug_recorder.reset();

View File

@@ -819,6 +819,50 @@ where
self.account_rlp_buf.clear();
}
/// Returns a heuristic for the total in-memory size of this state trie in bytes.
///
/// This aggregates the memory usage of the account trie, all revealed storage tries
/// (including cleared ones retained for allocation reuse), and auxiliary data structures.
pub fn memory_size(&self) -> usize {
let mut size = core::mem::size_of::<Self>();
size += match &self.state {
RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
t.memory_size()
}
RevealableSparseTrie::Blind(None) => 0,
};
for trie in self.storage.tries.values() {
size += match trie {
RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
t.memory_size()
}
RevealableSparseTrie::Blind(None) => 0,
};
}
for trie in &self.storage.cleared_tries {
size += match trie {
RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
t.memory_size()
}
RevealableSparseTrie::Blind(None) => 0,
};
}
size += self.revealed_account_paths.capacity() * core::mem::size_of::<Nibbles>();
for paths in self.storage.revealed_paths.values() {
size += paths.capacity() * core::mem::size_of::<Nibbles>();
}
size
}
/// Returns the number of storage tries currently retained (active + cleared).
pub fn retained_storage_tries_count(&self) -> usize {
self.storage.tries.len() + self.storage.cleared_tries.len()
}
/// Shrinks the capacity of the sparse trie to the given node and value sizes.
///
/// This helps reduce memory usage when the trie has excess capacity.

View File

@@ -288,6 +288,12 @@ pub trait SparseTrie: Sized + Debug + Send + Sync {
/// during pruning. Larger values indicate larger tries that are more valuable to preserve.
fn size_hint(&self) -> usize;
/// Returns a heuristic for the in-memory size of this trie in bytes.
///
/// This is an approximation that accounts for the trie's nodes, values,
/// and auxiliary data structures.
fn memory_size(&self) -> usize;
/// Replaces nodes beyond `max_depth` with hash stubs and removes their descendants.
///
/// Depth counts nodes traversed (not nibbles), so extension nodes count as 1 depth