Skip to content

Capture Windows benchmark results: benchmarks write their own result file - #119

Merged
emeryberger merged 1 commit into
masterfrom
ci/windows-benchmark-capture
Jul 14, 2026
Merged

Capture Windows benchmark results: benchmarks write their own result file#119
emeryberger merged 1 commit into
masterfrom
ci/windows-benchmark-capture

Conversation

@emeryberger

Copy link
Copy Markdown
Owner

Windows CI could see exit codes but never a number.

The problem

The benchmarks run under DLL injection (withdll.exe), and their stdout was not captured — hoard.txt held withdll's banner and nothing else. So Windows was crash-tested but never performance-tested, and a Windows performance regression would have gone completely unnoticed.

It's also why removing #define inline __forceinline (#117) couldn't be verified there: no number existed to look at.

What I found

alloc8#14 (merged) fixed the underlying handle bug: the Detours withdll sample launches the target with a zeroed STARTUPINFO, so it never sets STARTF_USESTDHANDLES and the target doesn't inherit a redirected stdout. That fix is correct and worth having on its own.

But it wasn't sufficient — with the fixed withdll in place, the benchmark output still didn't appear. Rather than keep bisecting a Windows-only path one slow CI cycle at a time, I made capture robust by construction.

The fix

Each benchmark appends its own result line to the file named by BENCH_OUT (bench_common.h's bench_report), opening and fclose-ing it itself:

static inline void bench_report(const char* fmt, ...) {
  ...vprintf(fmt, ap); fflush(stdout);          /* stdout: unchanged */
  const char* path = getenv("BENCH_OUT");
  if (path) { FILE* f = fopen(path, "a"); vfprintf(f, fmt, ap); fclose(f); }
}

That depends on neither handle inheritance nor the CRT flushing a buffer during teardown, so the result survives either way.

  • stdout behaviour is unchanged — Linux and macOS see exactly what they saw before (verified locally, with and without Hoard injected).
  • The Windows job now prints the results and fails loudly if the file is missing, so silence can no longer masquerade as success.

What this unlocks

Windows can now be performance-measured at all — which is the prerequisite for extending the QoS gate to it.

🤖 Generated with Claude Code

…file

Windows CI could see exit codes but never a number. The benchmarks run under
DLL injection (withdll.exe), and their stdout was not captured -- hoard.txt
held withdll's banner and nothing else. So Windows was crash-tested but never
performance-tested, and a Windows performance regression would have gone
completely unnoticed. (It is also why removing `#define inline __forceinline`
could not be verified there: no number existed to look at.)

alloc8#14 fixed the underlying handle bug -- the Detours withdll sample launches
the target with a zeroed STARTUPINFO, so it never sets STARTF_USESTDHANDLES and
the target does not inherit a REDIRECTED stdout. That fix is correct and worth
having.

But it is not sufficient on its own: with the fixed withdll in place, the
benchmark output still did not appear. Rather than keep bisecting a Windows-only
path through CI, make capture robust by construction: each benchmark appends its
own result line to the file named by BENCH_OUT (bench_common.h's bench_report),
opening and fclosing it itself. That depends on neither handle inheritance nor
on the CRT flushing a buffer during teardown, so the result survives either way.

stdout behaviour is unchanged, so Linux and macOS see exactly what they saw
before. The Windows job now prints the results and fails loudly if the file is
missing -- silence can no longer masquerade as success.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@emeryberger
emeryberger merged commit ac07584 into master Jul 14, 2026
5 checks passed
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