Skip to content

feat: run each benchmark in its own process - #57

Draft
not-matthias wants to merge 3 commits into
mainfrom
cod-3959-implement-process-isolation-in-codspeed-cpp
Draft

not-matthias wants to merge 3 commits into
mainfrom
cod-3959-implement-process-isolation-in-codspeed-cpp

Conversation

@not-matthias

Copy link
Copy Markdown
Member

TLDR: Benchmarks in one process share allocator and thread state, so results depend on which other benchmarks ran before them; now each benchmark runs in a fresh child process (Linux/macOS only; Windows errors out).

  • CODSPEED_PROCESS_ISOLATION: unset/1/true = one process per benchmark (default), 0/false = shared process, any other value = hard error.
  • The parent re-execs the same binary with the original argv and env plus CODSPEED_ISOLATED_BENCHMARK=<uri>, then waits for the child. Benchmarks run one after another.
  • The child runs only that benchmark, using the same measurement path as before, and does not print the mode/context header again.
  • Only CODSPEED_ANALYSIS builds (simulation/memory) are affected; walltime and off builds are untouched.
  • In the child, an exact match on the URI overrides --benchmark_filter.
  • If a child fails, the parent exits with the child's code (128+N for signal N).
  • Re-exec instead of fork(), so threads started in main exist in the child.
  • E2E test (ctest + Bazel sh_test, new CI job): 3 PIDs with isolation on, 1 PID with it off, failing benchmark exits with code 3, filter override, invalid value rejected.

Review notes

  • --benchmark_out and the JSON reporter are written per child; there is no aggregation in the parent.
  • On Bazel 9 the sh_test would need rules_shell.
  • One child per benchmark, even with repetitions.
  • Isolation is also active in analysis builds run outside the CodSpeed runner, same as Rust.

Mirrors the codspeed-rust implementation (CodSpeedHQ/codspeed-rust, process isolation)

Closes COD-3959

Benchmarks of one binary share a process, so the allocator state and
threads one benchmark leaves behind (raised mmap threshold, fragmented
bins, retained caches) change the measurement of every benchmark after
it. Adding or changing one benchmark can then shift unrelated results.

Add `codspeed::Isolation`. It is on by default and turned off with
`CODSPEED_PROCESS_ISOLATION=false` (or `0`); any other value than
true/1/false/0 is an error. `Isolation::run` starts the benchmark
executable again with the original arguments and
`CODSPEED_ISOLATED_BENCHMARK` set to one benchmark URI, so each benchmark
gets a fresh process. A failing child's exit code is propagated (128 + N
for signal N).

Re-exec instead of fork(): fork only copies the calling thread, so
threads the binary started before the harness would be missing in the
child and work sent to them would hang. Linux uses /proc/self/exe, macOS
_NSGetExecutablePath; Windows is not supported.
In analysis builds, store the original argv in `Initialize` and route
every benchmark through `Isolation::run`, one child per benchmark
including its repetitions. A child only matches its own benchmark name,
which overrides `--benchmark_filter`, and does not print the mode line
and context again. Walltime builds are unchanged.
Build a fixture in simulation mode and check that:
- each benchmark runs in its own process by default, including one that
  uses a thread started in main;
- CODSPEED_PROCESS_ISOLATION=false runs them all in one process;
- a failing benchmark fails the run with its exit code;
- the child ignores the command line filter;
- an invalid setting is rejected.

Register it with ctest and as a Bazel sh_test, and run it in CI.
@codspeed

codspeed Bot commented Oct 21, 2026

Copy link
Copy Markdown

Hooray! CodSpeed harness just leveled up!

The base and head of this comparison were measured with different runner settings, so their benchmark values are not directly comparable.

What changed between base and head:

  • CodSpeed runner v5 changed how benchmarks are measured (base 4.18.1 → head 5.5.0). View release notes

Re-run the base with the same settings to get a valid performance comparison.


Comparing cod-3959-implement-process-isolation-in-codspeed-cpp (b84d783) with main (76985b6)

Open in CodSpeed

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