feat(engine): add gas-bucketed sub-phase metrics for new_payload (#22210)

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: YK <chiayongkang@hotmail.com>
This commit is contained in:
Georgios Konstantopoulos
2026-02-15 19:35:59 -08:00
committed by GitHub
parent 11acd97982
commit 317f858bd4
2 changed files with 76 additions and 2 deletions

View File

@@ -34,6 +34,10 @@ pub struct EngineApiMetrics {
/// Metrics for EIP-7928 Block-Level Access Lists (BAL).
#[allow(dead_code)]
pub(crate) bal: BalMetrics,
/// Gas-bucketed execution sub-phase metrics.
pub(crate) execution_gas_buckets: ExecutionGasBucketMetrics,
/// Gas-bucketed block validation sub-phase metrics.
pub(crate) block_validation_gas_buckets: BlockValidationGasBucketMetrics,
}
impl EngineApiMetrics {
@@ -82,6 +86,22 @@ impl EngineApiMetrics {
self.executor.post_execution_histogram.record(elapsed);
}
/// Records execution duration into the gas-bucketed execution histogram.
pub fn record_block_execution_gas_bucket(&self, gas_used: u64, elapsed: Duration) {
let idx = GasBucketMetrics::bucket_index(gas_used);
self.execution_gas_buckets.buckets[idx]
.execution_gas_bucket_histogram
.record(elapsed.as_secs_f64());
}
/// Records state root duration into the gas-bucketed block validation histogram.
pub fn record_state_root_gas_bucket(&self, gas_used: u64, elapsed_secs: f64) {
let idx = GasBucketMetrics::bucket_index(gas_used);
self.block_validation_gas_buckets.buckets[idx]
.state_root_gas_bucket_histogram
.record(elapsed_secs);
}
/// Records the time spent waiting for the next transaction from the iterator.
pub fn record_transaction_wait(&self, elapsed: Duration) {
self.executor.transaction_wait_histogram.record(elapsed);
@@ -280,7 +300,8 @@ impl GasBucketMetrics {
.record(gas_used as f64 / elapsed.as_secs_f64());
}
fn bucket_index(gas_used: u64) -> usize {
/// Returns the bucket index for a given gas value.
pub(crate) fn bucket_index(gas_used: u64) -> usize {
GAS_BUCKET_THRESHOLDS
.iter()
.position(|&threshold| gas_used < threshold)
@@ -288,7 +309,7 @@ impl GasBucketMetrics {
}
/// Returns a human-readable label like `<5M`, `5-10M`, … `>40M`.
fn bucket_label(index: usize) -> String {
pub(crate) fn bucket_label(index: usize) -> String {
if index == 0 {
let hi = GAS_BUCKET_THRESHOLDS[0] / MEGAGAS;
format!("<{hi}M")
@@ -303,6 +324,56 @@ impl GasBucketMetrics {
}
}
/// Per-gas-bucket execution duration metric.
#[derive(Clone, Metrics)]
#[metrics(scope = "sync.execution")]
pub(crate) struct ExecutionGasBucketSeries {
/// Gas-bucketed EVM execution duration.
pub(crate) execution_gas_bucket_histogram: Histogram,
}
/// Holds pre-initialized [`ExecutionGasBucketSeries`] instances, one per gas bucket.
#[derive(Debug)]
pub(crate) struct ExecutionGasBucketMetrics {
buckets: [ExecutionGasBucketSeries; NUM_GAS_BUCKETS],
}
impl Default for ExecutionGasBucketMetrics {
fn default() -> Self {
Self {
buckets: std::array::from_fn(|i| {
let label = GasBucketMetrics::bucket_label(i);
ExecutionGasBucketSeries::new_with_labels(&[("gas_bucket", label)])
}),
}
}
}
/// Per-gas-bucket block validation metrics (state root).
#[derive(Clone, Metrics)]
#[metrics(scope = "sync.block_validation")]
pub(crate) struct BlockValidationGasBucketSeries {
/// Gas-bucketed state root computation duration.
pub(crate) state_root_gas_bucket_histogram: Histogram,
}
/// Holds pre-initialized [`BlockValidationGasBucketSeries`] instances, one per gas bucket.
#[derive(Debug)]
pub(crate) struct BlockValidationGasBucketMetrics {
buckets: [BlockValidationGasBucketSeries; NUM_GAS_BUCKETS],
}
impl Default for BlockValidationGasBucketMetrics {
fn default() -> Self {
Self {
buckets: std::array::from_fn(|i| {
let label = GasBucketMetrics::bucket_label(i);
BlockValidationGasBucketSeries::new_with_labels(&[("gas_bucket", label)])
}),
}
}
}
/// Metrics for engine newPayload responses.
#[derive(Metrics)]
#[metrics(scope = "consensus.engine.beacon")]

View File

@@ -597,6 +597,8 @@ where
};
self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64());
self.metrics
.record_state_root_gas_bucket(block.header().gas_used(), root_elapsed.as_secs_f64());
debug!(target: "engine::tree::payload_validator", ?root_elapsed, "Calculated state root");
// ensure state root matches
@@ -765,6 +767,7 @@ where
let execution_duration = execution_start.elapsed();
self.metrics.record_block_execution(&output, execution_duration);
self.metrics.record_block_execution_gas_bucket(output.result.gas_used, execution_duration);
debug!(target: "engine::tree::payload_validator", elapsed = ?execution_duration, "Executed block");
Ok((output, senders, result_rx))