From 4be22262352480539ba342d601f57681571f0953 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Thu, 19 Jun 2025 15:52:05 +0200 Subject: [PATCH] perf: Reuse CachedPrecompileMetrics across block executions (#16944) --- crates/engine/tree/src/tree/mod.rs | 7 +++- .../src/tree/payload_processor/prewarm.rs | 1 + .../engine/tree/src/tree/precompile_cache.rs | 38 ++++++++++++++----- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 982a8f5418..a6816d2931 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -18,7 +18,7 @@ use error::{InsertBlockError, InsertBlockErrorKind, InsertBlockFatalError}; use instrumented_state::InstrumentedStateProvider; use payload_processor::sparse_trie::StateRootComputeOutcome; use persistence_state::CurrentPersistenceAction; -use precompile_cache::{CachedPrecompile, PrecompileCacheMap}; +use precompile_cache::{CachedPrecompile, CachedPrecompileMetrics, PrecompileCacheMap}; use reth_chain_state::{ CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, MemoryOverlayStateProvider, NewCanonicalChain, @@ -276,6 +276,9 @@ where evm_config: C, /// Precompile cache map. precompile_cache_map: PrecompileCacheMap>, + /// Metrics for precompile cache, saved between block executions so we don't re-allocate for + /// every block. + precompile_cache_metrics: CachedPrecompileMetrics, } impl std::fmt::Debug @@ -370,6 +373,7 @@ where payload_processor, evm_config, precompile_cache_map, + precompile_cache_metrics: Default::default(), } } @@ -2443,6 +2447,7 @@ where precompile, self.precompile_cache_map.cache_for_address(*address), *self.evm_config.evm_env(block.header()).spec_id(), + Some(self.precompile_cache_metrics.clone()), ) }); } diff --git a/crates/engine/tree/src/tree/payload_processor/prewarm.rs b/crates/engine/tree/src/tree/payload_processor/prewarm.rs index 2153f6ee75..c69df3172f 100644 --- a/crates/engine/tree/src/tree/payload_processor/prewarm.rs +++ b/crates/engine/tree/src/tree/payload_processor/prewarm.rs @@ -275,6 +275,7 @@ where precompile, precompile_cache_map.cache_for_address(*address), spec_id, + None, // CachedPrecompileMetrics ) }); } diff --git a/crates/engine/tree/src/tree/precompile_cache.rs b/crates/engine/tree/src/tree/precompile_cache.rs index 47d985a929..066873d2c7 100644 --- a/crates/engine/tree/src/tree/precompile_cache.rs +++ b/crates/engine/tree/src/tree/precompile_cache.rs @@ -123,7 +123,7 @@ where /// The precompile. precompile: DynPrecompile, /// Cache metrics. - metrics: CachedPrecompileMetrics, + metrics: Option, /// Spec id associated to the EVM from which this cached precompile was created. spec_id: S, } @@ -133,30 +133,48 @@ where S: Eq + Hash + std::fmt::Debug + Send + Sync + Clone + 'static, { /// `CachedPrecompile` constructor. - pub(crate) fn new(precompile: DynPrecompile, cache: PrecompileCache, spec_id: S) -> Self { - Self { precompile, cache, spec_id, metrics: Default::default() } + pub(crate) const fn new( + precompile: DynPrecompile, + cache: PrecompileCache, + spec_id: S, + metrics: Option, + ) -> Self { + Self { precompile, cache, spec_id, metrics } } pub(crate) fn wrap( precompile: DynPrecompile, cache: PrecompileCache, spec_id: S, + metrics: Option, ) -> DynPrecompile { - let wrapped = Self::new(precompile, cache, spec_id); + let wrapped = Self::new(precompile, cache, spec_id, metrics); move |data: &[u8], gas_limit: u64| -> PrecompileResult { wrapped.call(data, gas_limit) } .into() } fn increment_by_one_precompile_cache_hits(&self) { - self.metrics.precompile_cache_hits.increment(1); + if let Some(metrics) = &self.metrics { + metrics.precompile_cache_hits.increment(1); + } } fn increment_by_one_precompile_cache_misses(&self) { - self.metrics.precompile_cache_misses.increment(1); + if let Some(metrics) = &self.metrics { + metrics.precompile_cache_misses.increment(1); + } + } + + fn set_precompile_cache_size_metric(&self, to: f64) { + if let Some(metrics) = &self.metrics { + metrics.precompile_cache_size.set(to); + } } fn increment_by_one_precompile_errors(&self) { - self.metrics.precompile_errors.increment(1); + if let Some(metrics) = &self.metrics { + metrics.precompile_errors.increment(1); + } } } @@ -180,7 +198,7 @@ where Ok(output) => { let key = CacheKey::new(self.spec_id.clone(), Bytes::copy_from_slice(data)); let size = self.cache.insert(key, CacheEntry(output.clone())); - self.metrics.precompile_cache_size.set(size as f64); + self.set_precompile_cache_size_metric(size as f64); self.increment_by_one_precompile_cache_misses(); } _ => { @@ -241,7 +259,7 @@ mod tests { .into(); let cache = - CachedPrecompile::new(dyn_precompile, PrecompileCache::default(), SpecId::PRAGUE); + CachedPrecompile::new(dyn_precompile, PrecompileCache::default(), SpecId::PRAGUE, None); let output = PrecompileOutput { gas_used: 50, @@ -298,11 +316,13 @@ mod tests { precompile1, cache_map.cache_for_address(address1), SpecId::PRAGUE, + None, ); let wrapped_precompile2 = CachedPrecompile::wrap( precompile2, cache_map.cache_for_address(address2), SpecId::PRAGUE, + None, ); // first invocation of precompile1 (cache miss)