Repository navigation
feat: run walltime benchmarks in forks - #101
Conversation
a33e439 to
4bd0611
Compare
Merging this PR will regress 3 benchmarks4 warnings, 1 note
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
[Medium impact] The change affects walltime measurement across the tinybench and Vitest 5 integrations. The PR still needs the previously reported fix that keeps Tinybench results and
|
860d65c to
8038822
Compare
8038822 to
86bdbbb
Compare
86bdbbb to
e9af7a1
Compare
adriencaccia
left a comment
There was a problem hiding this comment.
Main question is cost: with 3 forks by default, this repo's walltime benchmark step goes from 351 s on main to 1408 s on this PR. A run with forks should take about as long as one without; see the threads on the budget per fork, the per-fork overhead and the sample count of fast tinybench benchmarks. Vitest hooks must also keep working as they do today.
c376bfd to
3dca851
Compare
f4d1b47 to
f086cdf
Compare
adriencaccia
left a comment
There was a problem hiding this comment.
Earlier threads are addressed, or deferred to after the beta as agreed on COD-3960. Two Vitest issues come with the fork reporter (inline), and Greptile's bench.table() thread holds: in the walltime job on main every bench.table() prints its results, here every parent prints a table of nulls and each fork a table with only its own row.
5a0c4db to
d172321
Compare
18f0083 to
2e87449
Compare
Benchmarks in one process share allocator and JIT state, and rounds within one process are correlated, so a single process gives a biased view of a benchmark. Add the building blocks for integrations to run each walltime benchmark in several fresh processes (forks) and pool their rounds: - `walltimeIsolation(forks?)` picks the mode from the env: parent, fork (`CODSPEED_ISOLATED_BENCHMARK`), or one shared process when `CODSPEED_PROCESS_ISOLATION=false` - the fork count is `CODSPEED_WALLTIME_FORKS`, else the integration's `forks` option, else 3 - `runForks`/`runForksSync` re-run the benchmark command (keeping the V8 flags) once per fork and collect the samples each fork writes with `writeForkSamples`; a failing fork's exit code is passed on - `samplesToResult` builds tinybench-style statistics over the pooled samples Refs COD-3960
`withCodSpeed(bench, { forks })` sets the fork count of walltime runs, and
`CODSPEED_WALLTIME_FORKS` overrides it. The parent runs each task in its
forks and reports their pooled rounds; a fork runs only its task, hands its
samples over and prints no banner or summary. Simulation runs are unchanged.
Refs COD-3960
efb0428 to
d51f2ba
Compare
With Vitest 5, each walltime benchmark runs in fresh `vitest bench`
processes restricted to its file. The fork count resolves as
`CODSPEED_WALLTIME_FORKS`, the benchmark's or run's `forks` option,
`codspeedPlugin({ forks })`, then 3. The test timeout is scaled by the
env or plugin fork count, since every fork of a test runs within it.
Vitest's own report shows zeroed statistics for forked benchmarks; the
CodSpeed results hold the pooled rounds. Vitest 3/4 don't fork.
Closes COD-3960
d51f2ba to
60d0515
Compare
TLDR: Rounds within one process are correlated (JIT tier-up, heap sizing, allocator state), so walltime results depend on the process they ran in. Each walltime benchmark now runs in 3 fresh processes (forks) by default, and their rounds are pooled into one result (tinybench and Vitest 5).
Fork count, highest priority first:
CODSPEED_WALLTIME_FORKS=<n>bench(name, { forks }, fn)orbench.compare(..., { forks })withCodSpeed(bench, { forks }),codspeedPlugin({ forks })3CODSPEED_PROCESS_ISOLATION=falseruns everything in one process.How it works
execPath+execArgv+argv, so the runner's V8 flags survive) once per fork, withCODSPEED_ISOLATED_BENCHMARK=<uri>. The fork measures only that benchmark and writes its samples to a temp file. The parent pools them and writes the walltime result.Cost (
examples/with-typescript-esmtinybench, 9 benchmarks): 77 s with isolation off, 140 s with 1 fork, 412 s with 3 forks. Each fork pays process startup.Review notes
forksabove that count can still time out.BenchOptionstype doesn't includeforks, so the per-benchmark option needs a cast in TypeScript.Closes COD-3960