mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(evm, trie): more metrics (#11613)
This commit is contained in:
@ -4,6 +4,7 @@ use reth_metrics::{
|
|||||||
metrics::{Counter, Gauge, Histogram},
|
metrics::{Counter, Gauge, Histogram},
|
||||||
Metrics,
|
Metrics,
|
||||||
};
|
};
|
||||||
|
use reth_trie::updates::TrieUpdates;
|
||||||
|
|
||||||
/// Metrics for the `EngineApi`.
|
/// Metrics for the `EngineApi`.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -49,6 +50,8 @@ pub(crate) struct EngineMetrics {
|
|||||||
#[derive(Metrics)]
|
#[derive(Metrics)]
|
||||||
#[metrics(scope = "sync.block_validation")]
|
#[metrics(scope = "sync.block_validation")]
|
||||||
pub(crate) struct BlockValidationMetrics {
|
pub(crate) struct BlockValidationMetrics {
|
||||||
|
/// Total number of storage tries updated in the state root calculation
|
||||||
|
pub(crate) state_root_storage_tries_updated_total: Counter,
|
||||||
/// Histogram of state root duration
|
/// Histogram of state root duration
|
||||||
pub(crate) state_root_histogram: Histogram,
|
pub(crate) state_root_histogram: Histogram,
|
||||||
/// Latest state root duration
|
/// Latest state root duration
|
||||||
@ -57,7 +60,9 @@ pub(crate) struct BlockValidationMetrics {
|
|||||||
|
|
||||||
impl BlockValidationMetrics {
|
impl BlockValidationMetrics {
|
||||||
/// Records a new state root time, updating both the histogram and state root gauge
|
/// Records a new state root time, updating both the histogram and state root gauge
|
||||||
pub(crate) fn record_state_root(&self, elapsed_as_secs: f64) {
|
pub(crate) fn record_state_root(&self, trie_output: &TrieUpdates, elapsed_as_secs: f64) {
|
||||||
|
self.state_root_storage_tries_updated_total
|
||||||
|
.increment(trie_output.storage_tries_ref().len() as u64);
|
||||||
self.state_root_duration.set(elapsed_as_secs);
|
self.state_root_duration.set(elapsed_as_secs);
|
||||||
self.state_root_histogram.record(elapsed_as_secs);
|
self.state_root_histogram.record(elapsed_as_secs);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ use reth_chainspec::EthereumHardforks;
|
|||||||
use reth_consensus::{Consensus, PostExecutionInput};
|
use reth_consensus::{Consensus, PostExecutionInput};
|
||||||
use reth_engine_primitives::EngineTypes;
|
use reth_engine_primitives::EngineTypes;
|
||||||
use reth_errors::{ConsensusError, ProviderResult};
|
use reth_errors::{ConsensusError, ProviderResult};
|
||||||
use reth_evm::execute::{BlockExecutorProvider, Executor};
|
use reth_evm::execute::BlockExecutorProvider;
|
||||||
use reth_payload_builder::PayloadBuilderHandle;
|
use reth_payload_builder::PayloadBuilderHandle;
|
||||||
use reth_payload_primitives::{PayloadAttributes, PayloadBuilder, PayloadBuilderAttributes};
|
use reth_payload_primitives::{PayloadAttributes, PayloadBuilder, PayloadBuilderAttributes};
|
||||||
use reth_payload_validator::ExecutionPayloadValidator;
|
use reth_payload_validator::ExecutionPayloadValidator;
|
||||||
@ -2160,10 +2160,7 @@ where
|
|||||||
let block = block.unseal();
|
let block = block.unseal();
|
||||||
|
|
||||||
let exec_time = Instant::now();
|
let exec_time = Instant::now();
|
||||||
let output = self
|
let output = self.metrics.executor.execute_metered(executor, (&block, U256::MAX).into())?;
|
||||||
.metrics
|
|
||||||
.executor
|
|
||||||
.metered((&block, U256::MAX).into(), |input| executor.execute(input))?;
|
|
||||||
|
|
||||||
trace!(target: "engine::tree", elapsed=?exec_time.elapsed(), ?block_number, "Executed block");
|
trace!(target: "engine::tree", elapsed=?exec_time.elapsed(), ?block_number, "Executed block");
|
||||||
if let Err(err) = self.consensus.validate_block_post_execution(
|
if let Err(err) = self.consensus.validate_block_post_execution(
|
||||||
@ -2227,7 +2224,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let root_elapsed = root_time.elapsed();
|
let root_elapsed = root_time.elapsed();
|
||||||
self.metrics.block_validation.record_state_root(root_elapsed.as_secs_f64());
|
self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64());
|
||||||
debug!(target: "engine::tree", ?root_elapsed, ?block_number, "Calculated state root");
|
debug!(target: "engine::tree", ?root_elapsed, ?block_number, "Calculated state root");
|
||||||
|
|
||||||
let executed = ExecutedBlock {
|
let executed = ExecutedBlock {
|
||||||
|
|||||||
@ -372,7 +372,7 @@ where
|
|||||||
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
|
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
mut self,
|
mut self,
|
||||||
input: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
mut witness: F,
|
mut witness: F,
|
||||||
|
|||||||
@ -77,7 +77,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
self,
|
self,
|
||||||
input: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
witness: F,
|
witness: F,
|
||||||
@ -86,8 +86,8 @@ where
|
|||||||
F: FnMut(&State<DB>),
|
F: FnMut(&State<DB>),
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Self::Left(a) => a.execute_with_state_witness(input, witness),
|
Self::Left(a) => a.execute_with_state_closure(input, witness),
|
||||||
Self::Right(b) => b.execute_with_state_witness(input, witness),
|
Self::Right(b) => b.execute_with_state_closure(input, witness),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,12 +38,12 @@ pub trait Executor<DB> {
|
|||||||
/// The output of the block execution.
|
/// The output of the block execution.
|
||||||
fn execute(self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error>;
|
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
|
/// Executes the EVM with the given input and accepts a state closure that is invoked with
|
||||||
/// EVM state after execution.
|
/// the EVM state after execution.
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
self,
|
self,
|
||||||
input: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
witness: F,
|
state: F,
|
||||||
) -> Result<Self::Output, Self::Error>
|
) -> Result<Self::Output, Self::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(&State<DB>);
|
F: FnMut(&State<DB>);
|
||||||
@ -203,7 +203,7 @@ mod tests {
|
|||||||
Err(BlockExecutionError::msg("execution unavailable for tests"))
|
Err(BlockExecutionError::msg("execution unavailable for tests"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
self,
|
self,
|
||||||
_: Self::Input<'_>,
|
_: Self::Input<'_>,
|
||||||
_: F,
|
_: F,
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
//! Executor metrics.
|
//! Executor metrics.
|
||||||
//!
|
//!
|
||||||
//! Block processing related to syncing should take care to update the metrics by using e.g.
|
//! Block processing related to syncing should take care to update the metrics by using either
|
||||||
//! [`ExecutorMetrics::metered`].
|
//! [`ExecutorMetrics::execute_metered`] or [`ExecutorMetrics::metered_one`].
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use metrics::{Counter, Gauge, Histogram};
|
use metrics::{Counter, Gauge, Histogram};
|
||||||
use reth_execution_types::BlockExecutionInput;
|
use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput};
|
||||||
use reth_metrics::Metrics;
|
use reth_metrics::Metrics;
|
||||||
use reth_primitives::BlockWithSenders;
|
use reth_primitives::BlockWithSenders;
|
||||||
|
|
||||||
|
use crate::execute::Executor;
|
||||||
|
|
||||||
/// Executor metrics.
|
/// Executor metrics.
|
||||||
// TODO(onbjerg): add sload/sstore, acc load/acc change, bytecode metrics
|
// TODO(onbjerg): add sload/sstore
|
||||||
#[derive(Metrics, Clone)]
|
#[derive(Metrics, Clone)]
|
||||||
#[metrics(scope = "sync.execution")]
|
#[metrics(scope = "sync.execution")]
|
||||||
pub struct ExecutorMetrics {
|
pub struct ExecutorMetrics {
|
||||||
@ -18,31 +20,106 @@ pub struct ExecutorMetrics {
|
|||||||
pub gas_processed_total: Counter,
|
pub gas_processed_total: Counter,
|
||||||
/// The instantaneous amount of gas processed per second.
|
/// The instantaneous amount of gas processed per second.
|
||||||
pub gas_per_second: Gauge,
|
pub gas_per_second: Gauge,
|
||||||
|
|
||||||
/// The Histogram for amount of time taken to execute blocks.
|
/// The Histogram for amount of time taken to execute blocks.
|
||||||
pub execution_histogram: Histogram,
|
pub execution_histogram: Histogram,
|
||||||
/// The total amount of time it took to execute the latest block.
|
/// The total amount of time it took to execute the latest block.
|
||||||
pub execution_duration: Gauge,
|
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 {
|
impl ExecutorMetrics {
|
||||||
/// Execute the given block and update metrics for the execution.
|
fn metered<F, R>(&self, block: &BlockWithSenders, f: F) -> R
|
||||||
pub fn metered<F, R>(&self, input: BlockExecutionInput<'_, BlockWithSenders>, f: F) -> R
|
|
||||||
where
|
where
|
||||||
F: FnOnce(BlockExecutionInput<'_, BlockWithSenders>) -> R,
|
F: FnOnce() -> R,
|
||||||
{
|
{
|
||||||
let gas_used = input.block.gas_used;
|
|
||||||
|
|
||||||
// Execute the block and record the elapsed time.
|
// Execute the block and record the elapsed time.
|
||||||
let execute_start = Instant::now();
|
let execute_start = Instant::now();
|
||||||
let output = f(input);
|
let output = f();
|
||||||
let execution_duration = execute_start.elapsed().as_secs_f64();
|
let execution_duration = execute_start.elapsed().as_secs_f64();
|
||||||
|
|
||||||
// Update gas metrics.
|
// Update gas metrics.
|
||||||
self.gas_processed_total.increment(gas_used);
|
self.gas_processed_total.increment(block.gas_used);
|
||||||
self.gas_per_second.set(gas_used as f64 / execution_duration);
|
self.gas_per_second.set(block.gas_used as f64 / execution_duration);
|
||||||
self.execution_histogram.record(execution_duration);
|
self.execution_histogram.record(execution_duration);
|
||||||
self.execution_duration.set(execution_duration);
|
self.execution_duration.set(execution_duration);
|
||||||
|
|
||||||
output
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ impl<DB> Executor<DB> for NoopBlockExecutorProvider {
|
|||||||
Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP))
|
Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
self,
|
self,
|
||||||
_: Self::Input<'_>,
|
_: Self::Input<'_>,
|
||||||
_: F,
|
_: F,
|
||||||
|
|||||||
@ -66,26 +66,26 @@ impl<DB> Executor<DB> for MockExecutorProvider {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
self,
|
self,
|
||||||
_: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
_: F,
|
_: F,
|
||||||
) -> Result<Self::Output, Self::Error>
|
) -> Result<Self::Output, Self::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(&State<DB>),
|
F: FnMut(&State<DB>),
|
||||||
{
|
{
|
||||||
unimplemented!()
|
<Self as Executor<DB>>::execute(self, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_hook<F>(
|
fn execute_with_state_hook<F>(
|
||||||
self,
|
self,
|
||||||
_: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
_: F,
|
_: F,
|
||||||
) -> Result<Self::Output, Self::Error>
|
) -> Result<Self::Output, Self::Error>
|
||||||
where
|
where
|
||||||
F: OnStateHook,
|
F: OnStateHook,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
<Self as Executor<DB>>::execute(self, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -381,7 +381,7 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_with_state_witness<F>(
|
fn execute_with_state_closure<F>(
|
||||||
mut self,
|
mut self,
|
||||||
input: Self::Input<'_>,
|
input: Self::Input<'_>,
|
||||||
mut witness: F,
|
mut witness: F,
|
||||||
|
|||||||
@ -612,7 +612,7 @@ where
|
|||||||
let mut codes = HashMap::default();
|
let mut codes = HashMap::default();
|
||||||
|
|
||||||
let _ = block_executor
|
let _ = block_executor
|
||||||
.execute_with_state_witness(
|
.execute_with_state_closure(
|
||||||
(&block.clone().unseal(), block.difficulty).into(),
|
(&block.clone().unseal(), block.difficulty).into(),
|
||||||
|statedb| {
|
|statedb| {
|
||||||
codes = statedb
|
codes = statedb
|
||||||
|
|||||||
@ -275,7 +275,7 @@ where
|
|||||||
// Execute the block
|
// Execute the block
|
||||||
let execute_start = Instant::now();
|
let execute_start = Instant::now();
|
||||||
|
|
||||||
self.metrics.metered((&block, td).into(), |input| {
|
self.metrics.metered_one((&block, td).into(), |input| {
|
||||||
let sealed = block.header.clone().seal_slow();
|
let sealed = block.header.clone().seal_slow();
|
||||||
let (header, seal) = sealed.into_parts();
|
let (header, seal) = sealed.into_parts();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user