Conversation
Add a warm-up phase to `bench send`: ramp the send rate, hold at the target rate until chain-observable readiness holds (every proposer produced full blocks, block transaction counts plateaued, pool occupancy stable, minimum duration elapsed), then move the measurement origin (start block, started_at, metric and sample offsets) to that boundary without pausing load. Defaults are derived from multi-region Tempo runs and documented on each flag; `--warmup off` restores the previous behaviour and `--warmup 45s` gives a fixed phase.
Warm up at `--warmup-tps` (default 2k TPS) instead of the target so a cold network never sees the full 50k-100k load, ramp from the cap to `--tps` over the warm-up ramp once readiness holds, and add `bench send --duration` to end the measured window a fixed time after the boundary, dropping the queued backlog. The JSON report carries the measured window's `started_unix_ms`, and the warm-up summary reports the evaluation that ended it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bench sendnow warms the network up before the measured window instead of measuring from the first transaction.Multi-region Tempo runs (10 validators, 4 regions, ~50k target TPS, 90 s windows) showed three cold-start effects inside the measured window: the first multi-megabyte block body over each proposer→peer connection arrived 5–10x slower than later ones (block times of 1–2 s for the first ~15 blocks), execution caches and the node-side build estimators needed ~15 loaded blocks to converge, and an instant jump to the target rate filled every pool within two seconds and phase-locked transaction expiry to that burst. Together they pulled the headline TPS from a ~15k steady state down to 11.6k.
How it works
--warmup-ramp(the token bucket's rate is now adjustable while running). The warm-up rate is--tpscapped at--warmup-tps(default 2000), so a cold network never sees a 50k–100k target during startup.bench sendalready has (block polling via the query RPC,txpool_status):--warmup-proposalsblocks with at least half the running median transaction count,--warmup-stable-blocksblocks agrees with the previous window within--warmup-stable-tolerance,txpool_statuspending counts are stable over 10 s (only when the warm-up runs at the target rate; skipped when the endpoint lacks the method),--warmup-minelapsed.--warmup-maxends the warm-up regardless and flags the runtimeout.--tpsover--warmup-ramp, still inside the warm-up, so the measured window starts at full rate rather than with a step.start_block, the report'sstarted_at, and metric and sample offsets all move to that moment. Load never pauses. Metrics are excluded through a checkpoint rather than a counter reset, so exported counter series stay monotonic.--durationends the measured window a fixed time after the boundary: the source is stopped and the queued backlog dropped (requests already on the wire still complete). This lets harnesses over-provision transactions for a warm-up of unknown length.The warm-up keeps ticking while the sender drains its buffered backlog after the source ends, so the boundary can still be reached late in a run. If the source runs dry before the warm-up ends, the whole run is reported as before and the warm-up is flagged
source-exhausted.Flags and defaults
--warmup <auto|off|DURATION>autooffrestores the previous behaviour; a duration gives a fixed phase--warmup-tps--warmup-ramp--duration--warmup-min--warmup-max--warmup-proposals--warmup-proposers--rpc-urlcount--warmup-stable-blocks/--warmup-stable-tolerance--warmup-pool-checktxpool_statusis unsupportedThe derivations are repeated on the flags (
bench send --help) and on the constants inbench-core/src/warmup.rs.Reports
warmupobject (mode, outcome, duration, blocks, warm-up rate, hand-off ramp, per-proposer full-block counts, conditions at the decision, transactions in flight at the boundary) andstarted_unix_ms, the measured window's start, for harnesses.started_atis the measured window's start;warmup_*keys are added to the run metadata (no schema change).Verification
cargo fmt --check,cargo clippy --workspace --all-targets -- -D warnings, unit tests for the tracker (ramp, readiness, timeout, fixed, pool check, summary), metrics checkpointing, sample rebasing, rate-limiter rate changes, and CLI parsing.anvil --block-time 1withtxgen-ethereum generateat 20 TPS: fixed warm-up,autoreaching readiness (pool check pending → stable → ready at 11.6 s), readiness reached while draining the backlog after the source ended,--warmup off, the source-exhausted fallback, a capped warm-up (--warmup-tps 8→ hand-off ramp to 20 TPS → boundary) with--duration 8sending the window on time (382 queued transactions dropped), and an unlimited--tps 0target warming up at the cap. Block ranges, rebased time series and the JSONwarmupobject came out as expected.tests/send_auth.rsnow passes--warmup offbecause it pins the exact query-RPC traffic of a plain send.tests/call_replay.rs(anvil-version dependent) andtxgen-ethereum/tests/scenario_two_chain.rs; both fail identically onmain.Harness changes (separate PRs, depend on this one)
scripts/txgen_workload.shgeneratesduration + warmup max + rampseconds of workload, passes the warm-up flags and--duration, and takesstarted_at/start_blockfrom the txgen report.contrib/bench/txgen/helpers.nudoes the same for the single-runner bench (vault and zones presets keep--warmup off).warmup_*run metadata to show warm-up blocks as pre-run context.🤖 Generated with Claude Code