Skip to main content

reth_evm/
metrics.rs

1//! Executor metrics.
2use alloy_consensus::BlockHeader;
3use metrics::{Counter, Gauge, Histogram};
4use reth_metrics::Metrics;
5use reth_primitives_traits::{Block, FastInstant as Instant, RecoveredBlock};
6
7/// Executor metrics.
8#[derive(Metrics, Clone)]
9#[metrics(scope = "sync.execution")]
10pub struct ExecutorMetrics {
11    /// The total amount of gas processed.
12    pub gas_processed_total: Counter,
13    /// The instantaneous amount of gas processed per second.
14    pub gas_per_second: Gauge,
15    /// The Histogram for amount of gas used.
16    pub gas_used_histogram: Histogram,
17
18    /// The Histogram for amount of time taken to execute the pre-execution changes.
19    pub pre_execution_histogram: Histogram,
20    /// The Histogram for amount of time taken to wait for one transaction to be available.
21    pub transaction_wait_histogram: Histogram,
22    /// The Histogram for amount of time taken to execute one transaction.
23    pub transaction_execution_histogram: Histogram,
24    /// The Histogram for amount of time taken to execute the post-execution changes.
25    pub post_execution_histogram: Histogram,
26    /// The Histogram for amount of time taken to execute blocks.
27    pub execution_histogram: Histogram,
28    /// The total amount of time it took to execute the latest block.
29    pub execution_duration: Gauge,
30
31    /// The Histogram for number of accounts updated when executing the latest block.
32    pub accounts_updated_histogram: Histogram,
33    /// The Histogram for number of storage slots updated when executing the latest block.
34    pub storage_slots_updated_histogram: Histogram,
35    /// The Histogram for number of bytecodes updated when executing the latest block.
36    pub bytecodes_updated_histogram: Histogram,
37}
38
39impl ExecutorMetrics {
40    /// Helper function for metered execution
41    fn metered<F, R>(&self, f: F) -> R
42    where
43        F: FnOnce() -> (u64, R),
44    {
45        // Execute the block and record the elapsed time.
46        let execute_start = Instant::now();
47        let (gas_used, output) = f();
48        let execution_duration = execute_start.elapsed().as_secs_f64();
49
50        // Update gas metrics.
51        self.gas_processed_total.increment(gas_used);
52        self.gas_per_second.set(gas_used as f64 / execution_duration);
53        self.gas_used_histogram.record(gas_used as f64);
54        self.execution_histogram.record(execution_duration);
55        self.execution_duration.set(execution_duration);
56
57        output
58    }
59
60    /// Execute a block and update basic gas/timing metrics.
61    ///
62    /// This is a simple helper that tracks execution time and gas usage.
63    /// For more complex metrics tracking (like state changes), use the
64    /// metered execution functions in the engine/tree module.
65    pub fn metered_one<F, R, B>(&self, block: &RecoveredBlock<B>, f: F) -> R
66    where
67        F: FnOnce(&RecoveredBlock<B>) -> R,
68        B: Block,
69        B::Header: BlockHeader,
70    {
71        self.metered(|| (block.header().gas_used(), f(block)))
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use alloy_consensus::Header;
79    use alloy_primitives::B256;
80    use reth_ethereum_primitives::Block;
81    use reth_primitives_traits::Block as BlockTrait;
82
83    fn create_test_block_with_gas(gas_used: u64) -> RecoveredBlock<Block> {
84        let header = Header { gas_used, ..Default::default() };
85        let block = Block { header, body: Default::default() };
86        // Use a dummy hash for testing
87        let hash = B256::default();
88        let sealed = block.seal_unchecked(hash);
89        RecoveredBlock::new_sealed(sealed, Default::default())
90    }
91
92    #[test]
93    fn test_metered_one_updates_metrics() {
94        let metrics = ExecutorMetrics::default();
95        let block = create_test_block_with_gas(1000);
96
97        // Execute with metered_one
98        let result = metrics.metered_one(&block, |b| {
99            // Simulate some work
100            std::thread::sleep(std::time::Duration::from_millis(10));
101            b.header().gas_used()
102        });
103
104        // Verify result
105        assert_eq!(result, 1000);
106    }
107
108    #[test]
109    fn test_metered_helper_tracks_timing() {
110        let metrics = ExecutorMetrics::default();
111
112        let result = metrics.metered(|| {
113            // Simulate some work
114            std::thread::sleep(std::time::Duration::from_millis(10));
115            (500, "test_result")
116        });
117
118        assert_eq!(result, "test_result");
119    }
120}