Repository navigation
Conversation
Merging this PR will degrade performance by 58.02%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | sleep 1 |
245.7 µs | 585.4 µs | -58.02% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing cod-3722-ignore-process-spawning-overhead-in-exec-harness-simulation (a301550) with main (8368147)2
Footnotes
-
6 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. ↩
-
No successful run was found on
spike/cod-3440-memtrack-musl(ecbfb5b) during the generation of this report, somain(8368147) was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
c72e991 to
3c1b378
Compare
1af664e to
8fb4858
Compare
345f77b to
b0533e3
Compare
exec-harness injected a shared library into every benchmark to drive valgrind's instrumentation from inside the child. That only works on a dynamically linked executable, so statically linked benchmarks were silently unmeasurable, and it forced the harness to ship a `.so` next to its binary. The instrumentation is now toggled in exec-harness's own process, around the spawn: valgrind propagates the state across `fork`/`exec`, so the child is measured without anything being injected into it. The preload library, its compatibility check and the build script that produced it all go away, and the integration constants become plain consts. Since the child no longer switches instrumentation on for itself, exec-harness runs pass `--instr-atstart=inherit` to valgrind; with `no`, the child starts uninstrumented and the measurement comes back empty. Entrypoint runs keep the previous default. BREAKING CHANGE: exec-harness no longer ships a preload library. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both binaries kept their argument parsing and dispatch in `main.rs`, where nothing else can reach it. Move each into a `cli` module of its own crate and leave `main.rs` as a wrapper that installs a logger and calls `run_cli`. Nothing changes for the standalone binaries, but the runner can now link either CLI and dispatch it in-process. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The runner downloaded `exec-harness` and `memtrack` from GitHub releases at the start of a run, pinned by version, and `setup --mode memory` installed memtrack with `cargo install`. That is a network round-trip on every run, a version matrix to keep in sync, and two more artifacts to release. Link both crates instead and expose them as hidden `codspeed exec-harness` and `codspeed memtrack` subcommands, re-executing the current binary where the runner used to invoke the downloaded tool. They are dispatched before any runner setup: the re-exec happens in the benchmark's working directory, where an unrelated `codspeed.yaml` would otherwise abort the measurement. The binary installer, the memory setup no-op and the memtrack tool status go away. BREAKING CHANGE: `exec-harness` and `memtrack` are no longer downloaded or installed separately; the runner binary carries them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`exec-harness` and `memtrack` were released as their own artifacts. Nothing downloads them any more, so they stop being release units: their apt build dependencies move to the runner, which now builds the vendored libbpf and elfutils, and memtrack is depended on with its default features so the bundled subcommand carries the tracker. The released Linux artifacts are musl, and memtrack could not be built for them: `libbpf-sys` vendors elfutils, whose `configure` looks for `argp`, `obstack` and `fts`, none of which musl ships, and Debian's `musl-gcc` runs with `-nostdinc`, so the kernel UAPI headers libbpf needs are out of reach. The recipe lives in the cargo config so a plain `cargo build --target <arch>-unknown-linux-musl` works: seed the autoconf cache for the three checks, add a declarations-only `argp.h` stub on `CPATH`, add the UAPI header paths back through the per-target `CFLAGS`, and link `-lgcc` on aarch64 for libbpf's outline-atomic helpers. `close_range` goes through the raw syscall, since `libc` only declares it for glibc. CI builds both musl targets on native runners and asserts the artifact is static with `readelf`. The bundled subcommands no longer take `--version`, and the memtrack benchmarks call memtrack through the runner instead of installing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
memtrack runs as a subcommand of this binary, so the allocator set in its own `main.rs` no longer applies to it. Set mimalloc on the runner binary instead, which covers memtrack and the rest of the runner alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With memtrack bundled, `setcap` targeted the runner binary itself, so every `codspeed` invocation ran in glibc's secure-execution mode: `LD_*`, `TMPDIR` and similar variables were stripped before the runner could forward them to the benchmark, in every mode. Grant the capabilities to a copy of the binary in `~/.cache`, keyed by its ELF build id, and run `codspeed memtrack` from it. The copy is installed in one sudo call, since `~/.cache` can be root-owned, and copies of other builds are pruned once they are a day old. The runner keeps the user's environment and no longer holds the eBPF capabilities itself. The memtrack benchmarks call `codspeed memtrack` from PATH, so CI grants that binary its capabilities directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The job only needs to prove that both musl targets build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both ship inside the `codspeed` binary, so their own versions no longer mean anything. Move the version to `[workspace.package]` and have the runner, `exec-harness` and `memtrack` inherit it. The integration version `exec-harness` reports goes from 1.3.0 to the runner's. They still declare binaries, so opt them out of dist: sharing the runner's version, a `v*` tag would otherwise release them alongside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add set_executed_benchmark_for_pid, which passes an explicit pid to instrument_hooks_set_executed_benchmark instead of the calling process' own. set_executed_benchmark keeps its behavior and delegates to it. Bump instrument-hooks, whose valgrind instrument now writes a "Benchmark pid: <pid>" desc line in the dump part when that pid is not the calling process'. This also brings thread-safe C API exports and the callgrind_toggle_collect helper. Refs COD-3722 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b0533e3 to
a301550
Compare
|
There was a problem hiding this comment.
I think we shouldn't bump valgrind-codspeed in this PR, but rather do it when we're releasing the new runner (e.g. to make it easier revertable).
It shouldn't be needed, as you can use newer versions as an "experimental version". So locally or in CI you can just build from source or install the .deb
Make exec-harness declare the pid of the command it benchmarks, so its own spawning cost can be left out of simulation results.
Since #531, exec-harness turns instrumentation on in its own process and spawns the command, which inherits that state across fork and exec. The dumped part therefore also holds the harness's cost of spawning the command and waiting for it. That is a fixed overhead on every benchmark, about 207k instructions per command locally.
Changes:
set_executed_benchmark_for_pid(pid, uri).set_executed_benchmarkkeeps its behavior and delegates to it.Command::status(). In memory mode the pid only travels to the runner's FIFO, where nothing reads it for exec-harness.desc: Benchmark pid: <pid>in the part when the pid is not the caller's. This bump also brings the thread-safe C API exports and thecallgrind_toggle_collecthelper.Checked locally under the patched valgrind, with the runner's simulation flags on
sh -c '/bin/true; /bin/true; :':Still to do before this is ready:
0codspeed8once feat(callgrind): add CALLGRIND_ADD_DESC client request valgrind-codspeed#43 is released. Until then the request is ignored with a warning and results keep the harness's cost.Closes COD-3722