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