Skip to content

Benchmark suite with results in RESULTS.md (not in CI) - #9

Closed
ZweZeya wants to merge 4 commits into
test/end-to-end-from-cppfrom
bench/suite
Closed

ZweZeya wants to merge 4 commits into
test/end-to-end-from-cppfrom
bench/suite

Conversation

@ZweZeya

@ZweZeya ZweZeya commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #8 (test/end-to-end-from-cpp).

What this changes

Turns the one benchmark from #7 into a suite whose results are written to benchmarks/RESULTS.md. It is run by hand and deliberately not added to CI: shared runners are too noisy for timings to mean anything.

Five workloads in benchmarks/workloads/, each one source built as baseline (wtc::queue<T>, lifted and lowered back unchanged), optimized (plus --select-queue-impl) and, where it is safe, direct (spsc_queue<T> by hand, the ceiling):

Workload Measures
spsc_throughput one producer, one consumer, flat out (replaces queue_bench.cpp and spsc_direct.cpp)
spsc_latency push-to-pop latency at a paced 1M items/s: p50, p99, p99.9, max
spsc_payload a 64-byte user-defined element type
spsc_same_thread one thread pushes a batch and pops it: no contention, and a case a bounded ring would deadlock on
mpsc_control two producers: must not be substituted; bounds how much two builds differ by themselves

What keeps the numbers honest:

  • run.sh states, per workload, whether the optimized build must be substituted, and fails if it disagrees, so no column can silently measure the wrong program.
  • Every workload checks what it received (count, order, contents) and fails rather than reports.
  • Variants run interleaved, RUNS times. report.py writes medians, the spread, each run's p99, and the commit, toolchain, machine and pinning.
  • RESULTS.md is committed on its own, after a run, so the commit it names is the one that was measured.

The queue in each workload is a global touched only at the call sites (bench.hpp), because a helper taking it would be a use --select-queue-impl cannot retarget.

Results (M1 Pro, macOS, threads not pinned)

baseline optimized direct
spsc_throughput 2.65M/s 8.02M/s (3.0x) 43.6M/s (16.4x)
spsc_payload 2.65M/s 7.41M/s (2.8x) 28.8M/s (10.9x)
spsc_same_thread 3.13M/s 7.58M/s (2.4x) 38.6M/s (12.4x)
spsc_latency p50 / p99 2.6 µs / 992 µs 208 ns / 4.4 µs 166 ns / 1.5 µs

Three things the sessions showed that a single run would have hidden, all written into the README:

  • The control is never equal. Its two builds run the same queue code (the optimized one only has the shim's anchor erased), yet the optimized build was ahead by 6, 13 and 18 percent in three sessions, always in that direction and in either run order. That looks systematic (probably binary layout) rather than noise. Either way, a difference that size cannot be credited to the queue, and the report says so.
  • Tail latency varies a lot between runs. In one run of five, something else on the machine moved every variant's latency by orders of magnitude at once. Across undisturbed runs the substituted p99 ranged from about 2 to 23 µs. The report therefore lists each run's p99, and the claims in the README and in runtime/spsc_queue.hpp are worded to what all the runs support.
  • The gap between optimized and direct is mostly one shared_ptr allocation per pop, kept to match try_pop's interface.

How it was checked

All five workloads are substituted, or kept, as run.sh requires, and every variant's correctness checks passed on every run of three full sessions. check-wtc is unaffected (33 of 33). Not measured yet: pinned runs on Linux, bursty load, memory.

Checklist

  • CI is green
  • New behaviour has a test; a fixed bug has a test that failed before
  • No file moves or renames mixed in with substantive changes
  • The pinned LLVM revision is untouched, or this PR does nothing else

🤖 Generated with Claude Code

ZweZeya and others added 4 commits October 18, 2026 23:48
One workload, throughput only, printed to the terminal, said little: not
the tail the project's motivation rests on, not whether a gain survives a
bigger element type, and nothing about how much two runs differ by
themselves.

Five workloads, each one source built as baseline, optimized and direct:
spsc_throughput (the old benchmark), spsc_latency (push-to-pop latency at
a paced rate: p50, p99, p99.9, max), spsc_payload (a 64-byte record),
spsc_same_thread (one thread pushes a batch and pops it, which a bounded
ring would deadlock on), and mpsc_control (two producers, must not be
substituted). run.sh holds whether each optimized build must be
substituted and fails if it disagrees, so no column can silently measure
the wrong thing; each workload fails if what it received is wrong.

Variants run interleaved, RUNS times. report.py writes RESULTS.md: medians,
latency, spread, and the commit, toolchain, machine and pinning they came
from. The control's two builds bound the noise, and the report states it:
they run the same queue code and still measured 10 to 13 percent apart on
an M1 Pro, in either order, so the report says a difference that size
means nothing.

The queue in each workload is a global touched only at the call sites,
because a helper taking it would be a use --select-queue-impl cannot
retarget. queue_bench.cpp and spsc_direct.cpp are replaced by
spsc_throughput.cpp, which is both.

Not in CI, on purpose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first two sessions put the substituted queue's p99 at 1.3 µs and at
11 µs, and in one run of five every variant's latency jumped by orders of
magnitude at once. A median over five runs hides that, so the report now
lists each run's p99 beside the medians, and the claims in the README and
the runtime header say what the runs support rather than what the best
one did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apple M1 Pro, macOS, threads not pinned, five interleaved runs per
variant. Substituted throughput is 2.4 to 3.0 times the baseline's;
median push-to-pop latency falls from 2.6 µs to 208 ns and p99 from about
1 ms to 4 µs. The control measured 18 percent apart between two builds of
the same queue code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three sessions put mpsc_control's optimized build 6, 13 and 18 percent
ahead of its baseline, always in that direction and in either run order.
The README called that noise; it is a consistent difference between two
binaries that differ only by the erased anchor. The conclusion drawn from
it stands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@apaznikov

Copy link
Copy Markdown
Collaborator

Closing together with the stack below it, since the substitution it measures is replaced in #10. The benchmarks there run under a reservation protocol on the lab's server (reserved cores, siblings offline, governor fixed, turbo off, interleaved repetitions, bootstrap intervals), and #10 adds workloads for the inline queue and for batched pushes. The idea of keeping a generated RESULTS.md with the environment and every run's spread is a good one for that suite; the results of the latest legs are recorded under benchmarks/results/ in #10.

@apaznikov apaznikov closed this Oct 19, 2026
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.

2 participants