Skip to content

fix: keep one-time setup costs out of the first measured benchmark - #123

Draft
not-matthias wants to merge 2 commits into
mainfrom
warm-measurement-path-before-measuring
Draft

not-matthias wants to merge 2 commits into
mainfrom
warm-measurement-path-before-measuring

Conversation

@not-matthias

Copy link
Copy Markdown
Member

TLDR: In analysis mode, the first benchmark measured in a process included one-time setup costs: V8 compiling the root-frame wrapper (tinybench) and the dynamic linker resolving instrument_hooks_stop_benchmark (all integrations). Both now happen before measurement starts.

  • tinybench: the warmup calls the benchmark through the __codspeed_root_frame__ wrapper, so V8 compiles it before startBenchmark().
  • core: link the native addon with -z now, so its symbols resolve at load instead of on the first stopBenchmark().

Callgrind Ir, using the runner's simulation args:

Benchmark Before After
with-typescript-simple-esm switch 1 (first in process) 156,638 4,625
with-typescript-simple-esm switch 2 3,152 3,152
with-vitest-v5 fibo 15 recursive (first in process) 321,309 318,970

Review notes

Analysis runs called the `__codspeed_root_frame__` wrapper for the first
time after `startBenchmark()`, so V8 compiled it, and the perf map
recorded it, inside the measurement. Only the first benchmark of a process
paid for it: `switch 1` of examples/with-typescript-simple-esm measured
156,638 Ir under callgrind, against 3,152 Ir for `switch 2`.

The warmup now calls the benchmark through the same wrapper, which brings
`switch 1` down to 6,964 Ir.
With lazy binding, the first `stopBenchmark()` of a process resolved
`instrument_hooks_stop_benchmark` through the dynamic linker while the
benchmark was still measured. Link the addon with `-z now` so symbols
resolve when it loads.

Under callgrind this removes 2,339 Ir from the first benchmark of
examples/with-vitest-v5, and brings `switch 1` of
examples/with-typescript-simple-esm to 4,625 Ir.
@codspeed

codspeed Bot commented Nov 6, 2026

Copy link
Copy Markdown

Merging this PR will regress 5 benchmarks

4 warnings, 1 note

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ 7 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ 12 benchmarks with a significant change ran on different hardware

Their base and head results were measured on machines with a different CPU, core count or memory, so the changes might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

⚠️ 27 benchmarks with a significant change ran in a different software environment

Their base and head results were measured with a different OS or toolchain, such as compiler, runtime or library versions, so the changes might come from this difference rather than from the code. Open an affected benchmark to see what changed.

See affected benchmarks

ℹ️ 11 benchmarks have no flamegraph

The flamegraph wasn't generated: most of the time was spent in runtime internals, which would leave nothing useful to analyze.

See affected benchmarks

⚡ 22 improved benchmarks
❌ 5 regressed benchmarks
✅ 243 untouched benchmarks
⏩ 56 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ CPU Simulation wait 500ms 26.6 ms 48.3 ms -45%
❌ CPU Simulation wait 1sec 53.7 ms 86.9 ms -38.25%
❌ Memory wait 500ms 22 KB 27.5 KB -19.97%
❌ Walltime test sync baz 10 84 ns 96 ns -12.5%
❌ CPU Simulation switch 2 22.9 µs 25.5 µs -10.21%
⚡ CPU Simulation switch 1 663.1 µs 36.2 µs ×18
⚡ CPU Simulation switch 1 243 µs 21.8 µs ×11
⚡ CPU Simulation recursive fibo 10 678.3 µs 65.1 µs ×10
⚡ CPU Simulation switch 1 305.2 µs 36.1 µs ×8.5
⚡ Memory switch 1 33.6 KB 4.4 KB ×7.7
⚡ CPU Simulation recursive fibo 10 334.4 µs 65.2 µs ×5.1
⚡ CPU Simulation test_recursive_fibo_10 336.7 µs 66.7 µs ×5
⚡ CPU Simulation test_recursive_fibo_10 340.7 µs 70.4 µs ×4.8
⚡ CPU Simulation switch 1 307.7 µs 69.6 µs ×4.4
⚡ Memory wait 500ms 28.7 KB 9.4 KB ×3
⚡ Memory wait 1sec 28.7 KB 15.5 KB +85.31%
⚡ CPU Simulation RegExp#test 38.9 µs 26.9 µs +44.64%
⚡ CPU Simulation RegExp#test 39.1 µs 27.1 µs +44.48%
⚡ CPU Simulation RegExp#test 38.9 µs 27 µs +44.08%
⚡ CPU Simulation RegExp#test 38.8 µs 27 µs +43.59%
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing warm-measurement-path-before-measuring (329b067) with main (442e921)

Open in CodSpeed

Footnotes

  1. 56 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant