feat(evm, trie): more metrics (#11613)

This commit is contained in:
Alexey Shekhirin
2024-10-10 09:27:01 +01:00
committed by GitHub
parent a4b8150201
commit 58bfa60cea
11 changed files with 116 additions and 37 deletions

View File

@ -77,7 +77,7 @@ where
}
}
fn execute_with_state_witness<F>(
fn execute_with_state_closure<F>(
self,
input: Self::Input<'_>,
witness: F,
@ -86,8 +86,8 @@ where
F: FnMut(&State<DB>),
{
match self {
Self::Left(a) => a.execute_with_state_witness(input, witness),
Self::Right(b) => b.execute_with_state_witness(input, witness),
Self::Left(a) => a.execute_with_state_closure(input, witness),
Self::Right(b) => b.execute_with_state_closure(input, witness),
}
}

View File

@ -38,12 +38,12 @@ pub trait Executor<DB> {
/// The output of the block execution.
fn execute(self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error>;
/// Executes the EVM with the given input and accepts a witness closure that is invoked with the
/// EVM state after execution.
fn execute_with_state_witness<F>(
/// Executes the EVM with the given input and accepts a state closure that is invoked with
/// the EVM state after execution.
fn execute_with_state_closure<F>(
self,
input: Self::Input<'_>,
witness: F,
state: F,
) -> Result<Self::Output, Self::Error>
where
F: FnMut(&State<DB>);
@ -203,7 +203,7 @@ mod tests {
Err(BlockExecutionError::msg("execution unavailable for tests"))
}
fn execute_with_state_witness<F>(
fn execute_with_state_closure<F>(
self,
_: Self::Input<'_>,
_: F,

View File

@ -1,16 +1,18 @@
//! Executor metrics.
//!
//! Block processing related to syncing should take care to update the metrics by using e.g.
//! [`ExecutorMetrics::metered`].
//! Block processing related to syncing should take care to update the metrics by using either
//! [`ExecutorMetrics::execute_metered`] or [`ExecutorMetrics::metered_one`].
use std::time::Instant;
use metrics::{Counter, Gauge, Histogram};
use reth_execution_types::BlockExecutionInput;
use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput};
use reth_metrics::Metrics;
use reth_primitives::BlockWithSenders;
use crate::execute::Executor;
/// Executor metrics.
// TODO(onbjerg): add sload/sstore, acc load/acc change, bytecode metrics
// TODO(onbjerg): add sload/sstore
#[derive(Metrics, Clone)]
#[metrics(scope = "sync.execution")]
pub struct ExecutorMetrics {
@ -18,31 +20,106 @@ pub struct ExecutorMetrics {
pub gas_processed_total: Counter,
/// The instantaneous amount of gas processed per second.
pub gas_per_second: Gauge,
/// The Histogram for amount of time taken to execute blocks.
pub execution_histogram: Histogram,
/// The total amount of time it took to execute the latest block.
pub execution_duration: Gauge,
/// The Histogram for number of accounts loaded when executing the latest block.
pub accounts_loaded_histogram: Histogram,
/// The Histogram for number of storage slots loaded when executing the latest block.
pub storage_slots_loaded_histogram: Histogram,
/// The Histogram for number of bytecodes loaded when executing the latest block.
pub bytecodes_loaded_histogram: Histogram,
/// The Histogram for number of accounts updated when executing the latest block.
pub accounts_updated_histogram: Histogram,
/// The Histogram for number of storage slots updated when executing the latest block.
pub storage_slots_updated_histogram: Histogram,
/// The Histogram for number of bytecodes updated when executing the latest block.
pub bytecodes_updated_histogram: Histogram,
}
impl ExecutorMetrics {
/// Execute the given block and update metrics for the execution.
pub fn metered<F, R>(&self, input: BlockExecutionInput<'_, BlockWithSenders>, f: F) -> R
fn metered<F, R>(&self, block: &BlockWithSenders, f: F) -> R
where
F: FnOnce(BlockExecutionInput<'_, BlockWithSenders>) -> R,
F: FnOnce() -> R,
{
let gas_used = input.block.gas_used;
// Execute the block and record the elapsed time.
let execute_start = Instant::now();
let output = f(input);
let output = f();
let execution_duration = execute_start.elapsed().as_secs_f64();
// Update gas metrics.
self.gas_processed_total.increment(gas_used);
self.gas_per_second.set(gas_used as f64 / execution_duration);
self.gas_processed_total.increment(block.gas_used);
self.gas_per_second.set(block.gas_used as f64 / execution_duration);
self.execution_histogram.record(execution_duration);
self.execution_duration.set(execution_duration);
output
}
/// Execute the given block using the provided [`Executor`] and update metrics for the
/// execution.
///
/// Compared to [`Self::metered_one`], this method additionally updates metrics for the number
/// of accounts, storage slots and bytecodes loaded and updated.
pub fn execute_metered<'a, E, DB, O, Error>(
&self,
executor: E,
input: BlockExecutionInput<'a, BlockWithSenders>,
) -> Result<BlockExecutionOutput<O>, Error>
where
E: Executor<
DB,
Input<'a> = BlockExecutionInput<'a, BlockWithSenders>,
Output = BlockExecutionOutput<O>,
Error = Error,
>,
{
let output = self.metered(input.block, || {
executor.execute_with_state_closure(input, |state: &revm::db::State<DB>| {
// Update the metrics for the number of accounts, storage slots and bytecodes
// loaded
let accounts = state.cache.accounts.len();
let storage_slots = state
.cache
.accounts
.values()
.filter_map(|account| {
account.account.as_ref().map(|account| account.storage.len())
})
.sum::<usize>();
let bytecodes = state.cache.contracts.len();
// Record all state present in the cache state as loaded even though some might have
// been newly created.
// TODO: Consider spitting these into loaded and newly created.
self.accounts_loaded_histogram.record(accounts as f64);
self.storage_slots_loaded_histogram.record(storage_slots as f64);
self.bytecodes_loaded_histogram.record(bytecodes as f64);
})
})?;
// Update the metrics for the number of accounts, storage slots and bytecodes updated
let accounts = output.state.state.len();
let storage_slots =
output.state.state.values().map(|account| account.storage.len()).sum::<usize>();
let bytecodes = output.state.contracts.len();
self.accounts_updated_histogram.record(accounts as f64);
self.storage_slots_updated_histogram.record(storage_slots as f64);
self.bytecodes_updated_histogram.record(bytecodes as f64);
Ok(output)
}
/// Execute the given block and update metrics for the execution.
pub fn metered_one<F, R>(&self, input: BlockExecutionInput<'_, BlockWithSenders>, f: F) -> R
where
F: FnOnce(BlockExecutionInput<'_, BlockWithSenders>) -> R,
{
self.metered(input.block, || f(input))
}
}

View File

@ -51,7 +51,7 @@ impl<DB> Executor<DB> for NoopBlockExecutorProvider {
Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP))
}
fn execute_with_state_witness<F>(
fn execute_with_state_closure<F>(
self,
_: Self::Input<'_>,
_: F,

View File

@ -66,26 +66,26 @@ impl<DB> Executor<DB> for MockExecutorProvider {
})
}
fn execute_with_state_witness<F>(
fn execute_with_state_closure<F>(
self,
_: Self::Input<'_>,
input: Self::Input<'_>,
_: F,
) -> Result<Self::Output, Self::Error>
where
F: FnMut(&State<DB>),
{
unimplemented!()
<Self as Executor<DB>>::execute(self, input)
}
fn execute_with_state_hook<F>(
self,
_: Self::Input<'_>,
input: Self::Input<'_>,
_: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
unimplemented!()
<Self as Executor<DB>>::execute(self, input)
}
}