diff --git a/bin/reth-bench/README.md b/bin/reth-bench/README.md index e63f73875d..601b52f28e 100644 --- a/bin/reth-bench/README.md +++ b/bin/reth-bench/README.md @@ -32,7 +32,7 @@ Otherwise, running `make maxperf` at the root of the repo should be sufficient f The `reth-bench new-payload-fcu` command is the most representative of ethereum mainnet live sync, alternating between sending `engine_newPayload` calls and `engine_forkchoiceUpdated` calls. The `new-payload-fcu` command supports two optional waiting modes that can be used together or independently: -- `--wait-time `: Fixed sleep interval between blocks (e.g., `--wait-time 100ms`) +- `--wait-time `: Fixed sleep interval between blocks (e.g., `--wait-time 100ms` or `--wait-time 400` for 400ms) - `--wait-for-persistence`: Waits for blocks to be persisted using the `reth_subscribePersistedBlock` subscription When using `--wait-for-persistence`, the benchmark waits after every `(threshold + 1)` blocks, where the threshold defaults to the engine's persistence threshold (2). This can be customized with `--persistence-threshold `. diff --git a/bin/reth-bench/src/bench/helpers.rs b/bin/reth-bench/src/bench/helpers.rs index 8813082156..b9ae67d910 100644 --- a/bin/reth-bench/src/bench/helpers.rs +++ b/bin/reth-bench/src/bench/helpers.rs @@ -2,7 +2,10 @@ use crate::valid_payload::call_forkchoice_updated; use eyre::Result; -use std::io::{BufReader, Read}; +use std::{ + io::{BufReader, Read}, + time::Duration, +}; /// Read input from either a file path or stdin. pub(crate) fn read_input(path: Option<&str>) -> Result { @@ -51,6 +54,22 @@ pub(crate) fn parse_gas_limit(s: &str) -> eyre::Result { let base: u64 = num_str.trim().parse()?; base.checked_mul(multiplier).ok_or_else(|| eyre::eyre!("value overflow")) } + +/// Parses a duration string, treating bare integers as milliseconds. +/// +/// Accepts either a `humantime` duration string (e.g. `"100ms"`, `"2s"`) or a plain +/// integer which is interpreted as milliseconds (e.g. `"400"` → 400ms). +pub(crate) fn parse_duration(s: &str) -> eyre::Result { + match humantime::parse_duration(s) { + Ok(d) => Ok(d), + Err(_) => { + let millis: u64 = + s.trim().parse().map_err(|_| eyre::eyre!("invalid duration: {s:?}"))?; + Ok(Duration::from_millis(millis)) + } + } +} + use alloy_consensus::Header; use alloy_eips::eip4844::kzg_to_versioned_hash; use alloy_primitives::{Address, B256}; @@ -270,4 +289,24 @@ mod tests { assert!(parse_gas_limit("G").is_err()); assert!(parse_gas_limit("-1G").is_err()); } + + #[test] + fn test_parse_duration_with_unit() { + assert_eq!(parse_duration("100ms").unwrap(), Duration::from_millis(100)); + assert_eq!(parse_duration("2s").unwrap(), Duration::from_secs(2)); + assert_eq!(parse_duration("1m").unwrap(), Duration::from_secs(60)); + } + + #[test] + fn test_parse_duration_bare_millis() { + assert_eq!(parse_duration("400").unwrap(), Duration::from_millis(400)); + assert_eq!(parse_duration("0").unwrap(), Duration::from_millis(0)); + assert_eq!(parse_duration("1000").unwrap(), Duration::from_millis(1000)); + } + + #[test] + fn test_parse_duration_errors() { + assert!(parse_duration("abc").is_err()); + assert!(parse_duration("").is_err()); + } } diff --git a/bin/reth-bench/src/bench/new_payload_fcu.rs b/bin/reth-bench/src/bench/new_payload_fcu.rs index 086e997158..36a4f51dbc 100644 --- a/bin/reth-bench/src/bench/new_payload_fcu.rs +++ b/bin/reth-bench/src/bench/new_payload_fcu.rs @@ -12,6 +12,7 @@ use crate::{ bench::{ context::BenchContext, + helpers::parse_duration, output::{ write_benchmark_results, CombinedResult, NewPayloadResult, TotalGasOutput, TotalGasRow, }, @@ -25,7 +26,6 @@ use alloy_provider::Provider; use alloy_rpc_types_engine::ForkchoiceState; use clap::Parser; use eyre::{Context, OptionExt}; -use humantime::parse_duration; use reth_cli_runner::CliContext; use reth_engine_primitives::config::DEFAULT_PERSISTENCE_THRESHOLD; use reth_node_core::args::BenchmarkArgs; @@ -40,6 +40,9 @@ pub struct Command { rpc_url: String, /// How long to wait after a forkchoice update before sending the next payload. + /// + /// Accepts a duration string (e.g. `100ms`, `2s`) or a bare integer treated as + /// milliseconds (e.g. `400`). #[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, verbatim_doc_comment)] wait_time: Option, diff --git a/bin/reth-bench/src/bench/replay_payloads.rs b/bin/reth-bench/src/bench/replay_payloads.rs index 171615deec..0ee04a56d0 100644 --- a/bin/reth-bench/src/bench/replay_payloads.rs +++ b/bin/reth-bench/src/bench/replay_payloads.rs @@ -14,6 +14,7 @@ use crate::{ authenticated_transport::AuthenticatedTransportConnect, bench::{ + helpers::parse_duration, output::{ write_benchmark_results, CombinedResult, GasRampPayloadFile, NewPayloadResult, TotalGasOutput, TotalGasRow, @@ -30,7 +31,6 @@ use alloy_rpc_client::ClientBuilder; use alloy_rpc_types_engine::{ExecutionPayloadEnvelopeV4, ForkchoiceState, JwtSecret}; use clap::Parser; use eyre::Context; -use humantime::parse_duration; use reth_cli_runner::CliContext; use reth_engine_primitives::config::DEFAULT_PERSISTENCE_THRESHOLD; use reth_node_api::EngineApiMessageVersion; @@ -78,6 +78,9 @@ pub struct Command { output: Option, /// How long to wait after a forkchoice update before sending the next payload. + /// + /// Accepts a duration string (e.g. `100ms`, `2s`) or a bare integer treated as + /// milliseconds (e.g. `400`). #[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, verbatim_doc_comment)] wait_time: Option,