Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
d3caa8a
feat(bench): stub bench_common module with public API surface (Task 0.1)
elijahr May 1, 2026
cbc6892
feat(bench): implement BMFEmitter with alpha-sorted output (Task 0.2)
elijahr May 1, 2026
65e3c6b
feat(bench): implement Stats helpers (Task 0.4)
elijahr May 1, 2026
dff9a84
feat(bench): implement Histogram top-K + reservoir percentile (Task 0.3)
elijahr May 1, 2026
22063d5
feat(bench): implement runThroughputHarness (Task 0.5)
elijahr May 1, 2026
ddf8318
feat(bench): implement runLatencyHarness (Task 0.6)
elijahr May 1, 2026
71f2133
feat(bench): add merge_bmf.py BMF JSON merge utility (Task 0.7)
elijahr May 1, 2026
fafbddd
feat(bench): add 5 missing lockfreequeues adapters (Task 0.8)
elijahr May 1, 2026
02231a5
PR 0 Task 0.9: rename existing adapters with `_adapter` suffix and ex…
elijahr May 1, 2026
eb91ff4
PR 0 Task 0.10: native BMF emission via --bmf-out= flag in bench_thro…
elijahr May 1, 2026
3244691
PR 0 Task 0.11: bench.yml — native BMF + merge step, drop legacy parser
elijahr May 1, 2026
0eb0747
PR 0 Task 0.12: delete legacy bmf_adapter.py + bench_main.nim, refact…
elijahr May 1, 2026
c44d43f
PR 0 Task 0.13: render_readme.nim consumes new BMF shape
elijahr May 1, 2026
6d725c6
PR 0 Task 0.14: CHANGELOG entry under [Unreleased] for bench-rollup PR 0
elijahr May 1, 2026
44901c6
fix(bench): plug heap-allocated queue leaks in latency and throughput…
elijahr May 2, 2026
b330905
fix(bench): destroy queue before manager; guard zero-duration throughput
elijahr May 2, 2026
05268e7
fix(bench): guard zero-thread harness; expose adapter queues; drop no…
elijahr May 2, 2026
a4b1ad9
perf(bench): bulk-percentile to avoid 5x reservoir sort per latency run
elijahr May 3, 2026
ca00905
fix(render_readme): bump row cap; restore platform/cores/timestamp line
elijahr May 3, 2026
31caf8a
ci: pin sibling-dep clones to nim-debra v0.6.0 / nim-typestates v0.7.0
elijahr May 3, 2026
bd08d14
ci(bench): cost gate — drop feat/** PR base; require `bench` label
elijahr May 3, 2026
a619494
docs(bench): align Quick Start CI command with current 1M/500k budget
elijahr May 5, 2026
38bfc2a
ci(bench): wire t_bench_common into nimble benchtests + CI job
elijahr May 5, 2026
26e140e
PR 1 Task 1.1: BenchLatencyRuns/BenchLatencyMessageCount intdefines
elijahr May 1, 2026
e70d630
PR 1 Task 1.2: rewrite bench_latency.nim onto bench_common.runLatency…
elijahr May 1, 2026
833deac
PR 1 Tasks 1.3 + 1.4: bench-latency job + N-input merge step in bench…
elijahr May 1, 2026
34b62a2
PR 1 Task 1.5: smoke test for multi-measure-per-slug merge
elijahr May 1, 2026
2b6ff9b
PR 1 Task 1.7: CHANGELOG entry under [Unreleased] for bench-rollup PR 1
elijahr May 1, 2026
0452fc4
fix(bench-latency): correct prefix on unknown-flag error; rename grou…
elijahr May 2, 2026
9586c6b
fix(bench): grant bench-upload contents/actions read; isolate artifac…
elijahr May 2, 2026
3e26b6c
fix(bench): print p999 in latency stdout for operator visibility
elijahr May 3, 2026
e8fcca9
test(bench_latency): isolate test workspaces with createTempDir + ExeExt
elijahr May 3, 2026
2684687
ci(bench-latency): pin sibling deps to v0.6.0/v0.7.0; wire t_bench_la…
elijahr May 5, 2026
75d124f
PR 2 Task 2.1: capture pre-split slug-set fixture for deletion-safety
elijahr May 1, 2026
2df9466
PR 2 Task 2.3: bench_spsc binary (Sipsic 1p1c)
elijahr May 1, 2026
f3e1427
PR 2 Task 2.4: bench_mpsc binary (Mupsic 1p1c, 2p1c, 4p1c)
elijahr May 1, 2026
62a54ad
PR 2 Task 2.5: bench_mpmc binary (Mupmuc grid + 8p8c, Sipmuc, channels)
elijahr May 1, 2026
e8d3f5f
PR 2 Task 2.6: bench_unbounded binary (4 unbounded variants)
elijahr May 1, 2026
5615613
PR 2 Task 2.7: superset_check.py + deletion-safety wiring
elijahr May 1, 2026
0523acd
PR 2 Task 2.8: bench.yml matrix over 5 topology binaries + per-step t…
elijahr May 1, 2026
3686f51
PR 2 Task 2.9: 5-input union test in test_merge_bmf.py
elijahr May 1, 2026
7085bd7
PR 2 Task 2.10: delete bench_throughput.nim, rewire consumers
elijahr May 1, 2026
4bada5e
PR 2 Task 2.11: CHANGELOG entry under [Unreleased] for bench-rollup PR 2
elijahr May 1, 2026
6f71cc5
fix(bench): tighten bench_unbounded CI shape to fit 18-min budget
elijahr May 1, 2026
a81d483
fix(bench): gate oversubscribed C>=4 unbounded shapes behind a define
elijahr May 2, 2026
e896c6d
fix(bench): document p95 in BMF schema; clean up runner.py fragment f…
elijahr May 2, 2026
bd4ed7b
fix(bench): run upload on partial failures; correct timeout-comment
elijahr May 2, 2026
0fb3791
fix(bench): drop unparseable BMF fragments before merge
elijahr May 2, 2026
b9129ce
perf(bench): backoffOnPeerWait in busy-spin loops; document teardown …
elijahr May 3, 2026
04a4e42
docs(bench_unbounded): defend `create(T)` as alloc0, not destructor-o…
elijahr May 3, 2026
ab19733
fix(bench): pass -d:danger in runner.py build_nim() to match CI
elijahr May 5, 2026
4ff8aa8
test: t_unbounded_padding red — segment alloc returns 16B-aligned, ex…
elijahr May 1, 2026
8967192
feat(internal): add aligned_alloc.allocAligned via posix_memalign
elijahr May 1, 2026
dde5724
fix(unbounded): pad Segment fields and base-align via posix_memalign
elijahr May 1, 2026
ef378f2
feat(bench): add loony_adapter (unbounded MPMC) behind compile gate
elijahr May 1, 2026
cf7a324
feat(bench): add boost_lockfree_queue adapter (MPMC bounded, nim cpp)
elijahr May 1, 2026
42e3ded
feat(bench): add boost_lockfree_spsc adapter (SPSC bounded, nim cpp)
elijahr May 1, 2026
8ef5218
feat(bench): add bench-ffi-crossbeam Rust cdylib (ArrayQueue + SegQueue)
elijahr May 1, 2026
5458d38
feat(bench): add crossbeam_array_queue adapter (MPMC bounded, FFI cdy…
elijahr May 1, 2026
f3de15c
feat(bench): add crossbeam_seg_queue adapter (MPMC unbounded, FFI cdy…
elijahr May 1, 2026
672c2a2
feat(bench): wire MVP comparison adapters into bench_spsc/mpmc/unbounded
elijahr May 1, 2026
a45dc02
ci(bench): add boost+loony soft-skip flow per design §2.6
elijahr May 1, 2026
123fddc
ci(bench): add bench-comparison.yml (Crossbeam-only nightly cron)
elijahr May 1, 2026
b064282
docs: comparison MVP section in benchmarks/README + THIRD_PARTY_LICENSES
elijahr May 1, 2026
66d92f6
docs(changelog): add Track 3 (Comparison MVP) entries under [Unreleased]
elijahr May 1, 2026
aa77352
fix(bench): align Loony and Crossbeam slug prefixes with 3-segment ta…
elijahr May 1, 2026
d713fd9
fix(unbounded): use allocAligned for auto-create manager allocations
elijahr May 2, 2026
eb5aa12
fix(loony-adapter): close empty/pop TOCTOU window; document encoding …
elijahr May 2, 2026
d644170
fix(bench): honor alignof(T); explicit token perms; loony encoding range
elijahr May 2, 2026
28f2279
fix(unbounded): Windows alloc, head padding, finally-block teardown
elijahr May 3, 2026
9c9ced6
fix(bench/crossbeam): emit link flags from shared module
elijahr May 3, 2026
41a08f3
fix(bench-comparison): pin Nim sibling deps; abort-on-FFI-panic; corr…
elijahr May 5, 2026
d36775b
ci(bench): wire t_bench_adapters into nimble benchtests
elijahr May 5, 2026
977583e
feat(bench): add 4 PR-4 comparison adapters + vendored MoodyCamel
elijahr May 1, 2026
cfd28b7
feat(bench): wire 4 PR-4 comparison adapters into bench binaries
elijahr May 1, 2026
9ab02ba
ci(bench): add PR-4 install + soft-skip steps for 4 new adapters
elijahr May 1, 2026
6a31a36
docs(bench): PR-4 license, vendoring, README, CHANGELOG entries
elijahr May 1, 2026
c048389
fix(bench): use canonical 3-segment slug shape for comparison adapters
elijahr May 1, 2026
b973986
fix(adapters): standardize make/cleanup naming; reflect T in moodycam…
elijahr May 2, 2026
367d41a
fix(moodycamel): nothrow init; fail fast on nullptr handle
elijahr May 2, 2026
b849bcc
fix(moodycamel): cast for portability; clamp 32-bit capacity hint
elijahr May 3, 2026
9ba12b0
fix(moodycamel-adapter): static-assert sizeof(T)==8 and reject ref types
elijahr May 3, 2026
9b3e9f2
docs(threading-channels-adapter): drop reference to non-existent dein…
elijahr May 5, 2026
8ab5624
chore(docs): vendor uPlot 1.6.27 IIFE bundle for bench charts
elijahr May 1, 2026
b73c814
feat(docs): add bench-charts.js + CSS + BMF contract test
elijahr May 1, 2026
1699700
feat(docs): embed uPlot chart in benchmarks.md + register page in nav
elijahr May 1, 2026
3d383da
ci(bench): add paths-ignore + actor-guard loop-prevention scaffolding
elijahr May 1, 2026
48044ec
ci(bench): publish merged BMF snapshot to docs assets on devel push
elijahr May 1, 2026
1690a53
ci(docs): verify mike asset endpoint after dev deploy
elijahr May 1, 2026
7bc0550
docs(license): record uPlot vendoring + extend .gitattributes
elijahr May 1, 2026
fc51767
docs(readme): hand-curate BENCHMARKS summary; remove render_readme.nim
elijahr May 1, 2026
d18490b
docs(changelog): record PR 5 (interactive uPlot charts) under Unreleased
elijahr May 1, 2026
c30e554
fix(docs): correct chart asset paths; reuse ResizeObserver and destro…
elijahr May 2, 2026
a1bb5ac
fix(docs): retry snapshot push; portable Pages URL; drop non-positive Y
elijahr May 2, 2026
a27b171
fix(bench): dedupe contents permission key in bench-upload
elijahr May 2, 2026
36af7f2
fix(charts): vendor uPlot CSS, optimize sort, math.isfinite
elijahr May 3, 2026
c35dbe3
fix(charts): composite series key, O(1) tooltip lookup, drop dup CSS …
elijahr May 3, 2026
b01168c
fix(bench): drop [skip ci] on snapshot push; loosen SLUG_RE to match …
elijahr May 5, 2026
8c1290d
feat(bench): latency thresholds + p999/max measures + K=5000
elijahr May 1, 2026
4c2bfa4
docs(bench): correct K=5000 rationale; harness averages per-run perce…
elijahr May 2, 2026
1920490
ci(bench): execute bench harness tests via nimble benchtests
elijahr May 2, 2026
a4a147e
docs(bench_latency): correct max-vs-heap-head comment
elijahr May 3, 2026
a699801
Switch from PR-Agent to Momus
elijahr May 3, 2026
462db8b
Update momus workflow: rename pr-review.yml -> momus.yml, OPENROUTER_…
elijahr May 3, 2026
bd45c5f
test(aligned_alloc): use freeAligned symmetrically with allocAligned
elijahr May 5, 2026
eb56a15
chore(release): cut 4.2.0
elijahr May 5, 2026
1850919
Revert "chore(release): cut 4.2.0"
elijahr May 5, 2026
82d807c
Merge devel (typestates 0.7 uplift) into bench-rollup
elijahr May 5, 2026
2486b48
ci: track nim-debra/nim-typestates main, not stale tags
elijahr May 5, 2026
7be39db
Address Momus C findings: comparison threshold + percentile docstring
elijahr May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,6 @@ jobs:
- name: Vendor unittest2
run: git clone --depth 1 https://github.com/status-im/nim-unittest2.git deps/unittest2

- name: Run adapter unit tests
# Cheap pre-flight: fail fast if the parser itself is broken before
# spending minutes compiling and running the bench binary.
run: python3 benchmarks/test_bmf_adapter.py -v

- name: Compile bench_throughput (CI-tuned run shape)
# Cloud runs use a tighter wall-clock budget than the local default
# (1M messages × 33 runs). Bounded variants (sipsic, channels) use
Expand Down Expand Up @@ -113,14 +108,23 @@ jobs:
# Step-level timeout: if any single variant hangs, fail fast inside
# the 30-min job budget so Bencher upload steps still run (or are
# visibly skipped) instead of burning the entire budget on one variant.
# `--bmf-out=throughput.json` emits Bencher Metric Format JSON
# natively (Task 0.10); the legacy stdout->bmf_adapter.py regex
# parser is gone (Task 0.12).
timeout-minutes: 20
run: ./.tmp/bench_throughput sipsic mupmuc unbounded_mupsic channels | tee bench_output.txt
run: |
./.tmp/bench_throughput --bmf-out=throughput.json \
sipsic mupmuc unbounded_mupsic channels | tee bench_output.txt

- name: Convert to BMF JSON
run: python3 benchmarks/bmf_adapter.py bench_output.txt bench_results.json
- name: Merge BMF JSON
# Single-input merge today, but the bench-rollup feature splits
# bench_throughput into per-topology binaries in PR 2-4 each
# producing its own throughput.json fragment. Calling merge_bmf.py
# now keeps the workflow unchanged when those fragments arrive.
run: python3 benchmarks/merge_bmf.py merged.json throughput.json

- name: Show BMF JSON (debug)
run: cat bench_results.json
run: cat merged.json

- name: Install Bencher CLI
uses: bencherdev/bencher@main
Expand All @@ -132,7 +136,7 @@ jobs:
# silently appearing to "do nothing" with the data.
run: |
if [ -z "$BENCHER_API_TOKEN" ]; then
echo "::warning title=Bencher upload skipped::BENCHER_API_TOKEN secret is not set on this repo. The bench ran successfully and produced bench_results.json (visible in the 'Show BMF JSON (debug)' step), but no data is being uploaded to Bencher.dev. Set up the project at bencher.dev with slug 'lockfreequeues' and add BENCHER_API_TOKEN as a repo secret to enable upload."
echo "::warning title=Bencher upload skipped::BENCHER_API_TOKEN secret is not set on this repo. The bench ran successfully and produced merged.json (visible in the 'Show BMF JSON (debug)' step), but no data is being uploaded to Bencher.dev. Set up the project at bencher.dev with slug 'lockfreequeues' and add BENCHER_API_TOKEN as a repo secret to enable upload."
else
echo "Bencher token present; proceeding with upload."
fi
Expand All @@ -151,7 +155,7 @@ jobs:
--start-point-reset \
--testbed ubuntu-latest \
--adapter json \
--file bench_results.json \
--file merged.json \
--github-actions '${{ secrets.GITHUB_TOKEN }}' \
--err

Expand All @@ -170,7 +174,7 @@ jobs:
--threshold-lower-boundary 0.99 \
--thresholds-reset \
--adapter json \
--file bench_results.json \
--file merged.json \
--github-actions '${{ secrets.GITHUB_TOKEN }}' \
--err

Expand All @@ -184,4 +188,4 @@ jobs:
--branch "${GITHUB_REF##*/}" \
--testbed ubuntu-latest \
--adapter json \
--file bench_results.json
--file merged.json
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ nim-unittest2/
logs/
test_typed_introspection*
benchmarks/nim/bench_latency
benchmarks/nim/bench_main
benchmarks/nim/bench_throughput

# Compiled benchmark test binaries (extensionless executables)
Expand Down
77 changes: 77 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,83 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- New `benchmarks/nim/bench_common.nim` shared harness module exporting:
`Topology` enum, `BMFEmitter` (alpha-sorted Bencher Metric Format JSON
emission), `Histogram` (min-heap top-K + Algorithm R reservoir for
stratified-percentile estimation, p99 within 1% of sort fallback on
100k log-normal samples), generic `runThroughputHarness` and
`runLatencyHarness` (1P/1C ping-pong RTT with monotonic-ns timing and
per-run percentile aggregation), and Stats helpers (mean / stddev /
minVal / maxVal / linear-interpolation percentile).
- Five new lockfreequeues adapters in `benchmarks/nim/adapters/`:
`lockfreequeues_sipmuc_adapter.nim`, `lockfreequeues_mupsic_adapter.nim`,
`lockfreequeues_unbounded_sipsic_adapter.nim`,
`lockfreequeues_unbounded_sipmuc_adapter.nim`,
`lockfreequeues_unbounded_mupmuc_adapter.nim`. Each exposes
`topologiesSupported: set[Topology]` and the standard `push`/`pop`
shape consumed by the shared harness. The unbounded adapters store
the queue inline (not via `ptr`) to dodge a Nim 2.2.6 codegen bug
triggered by generic-pointer destructor calls when bench_common is
imported.
- New `benchmarks/merge_bmf.py` CLI: stateless union of per-binary BMF
JSON fragments into a single output file. Exits 1 on `(slug, measure)`
collisions naming both colliding inputs in stderr. Output slugs and
measures alpha-sorted. Pure-stdlib (no third-party deps); covered by
`benchmarks/tests/test_merge_bmf.py` (10 tests).
- `bench_throughput` `--bmf-out=<path>` flag emits Bencher Metric Format
JSON natively. The flag is purely additive: with the flag absent, the
binary is bit-for-bit unchanged from the prior release (same stdout
text, same positional CLI: `bench_throughput sipsic mupmuc
unbounded_mupsic channels`). Emitted slugs:
`lockfreequeues_sipsic/spsc/1p1c`,
`lockfreequeues_mupmuc/mpmc/{1,2,4,8}p{1,2,4,8}c`,
`lockfreequeues_unbounded_mupsic/mpsc_unbounded/{1,2,4}p1c`,
`nim_channels/mpmc/{1,2,4}p{1,2,4}c`. Each carries a
`throughput_ops_ms` measure with `value=mean`, `lower_value=mean-stddev`,
`upper_value=mean+stddev`.
- Per-variant compile-time run-count overrides:
`-d:BenchSipsicRuns=N`, `-d:BenchSipsicWarmup=N`,
`-d:BenchMupmucRuns=N`, `-d:BenchMupmucWarmup=N`,
`-d:BenchChannelsRuns=N`, `-d:BenchChannelsWarmup=N`. Defaults match
the prior hard-coded `runs = 10`, so production runs are unchanged.

### Changed

- `bench_throughput.nim` now natively emits Bencher Metric Format JSON
via `--bmf-out=<path>`. The CI workflow (`.github/workflows/bench.yml`)
was rewired to consume the native output and feed it through
`merge_bmf.py` before uploading to Bencher.dev — the previous Python
regex parser (`bmf_adapter.py`) is gone.
- The four existing lockfreequeues adapter files renamed to the
canonical `<library_slug>_adapter.nim` convention with `git mv`
(history preserved): `lockfreequeues_sipsic.nim`,
`lockfreequeues_mupmuc.nim`, `lockfreequeues_unbounded_mupsic.nim`.
Each gained a `topologiesSupported: set[Topology]` constant for the
upcoming PR 3 binary-split.
- `benchmarks/render_readme.nim` rewritten to consume the new BMF JSON
shape directly (`{slug: {measure: MeasureValue}}`) instead of the
legacy `bench_main` aggregator output. The slug walk decomposes
`<lib>/<topology>/<P>p<C>c` back into the (impl, thread_config) pair
the table renders.
- `benchmarks/runner.py` and `lockfreequeues.nimble` `task benchmarks`
redirected from `bench_main` to `bench_throughput --bmf-out=<path>`.
- `benchmarks/README.md` rewritten to document the new flow
(bench_common module, adapter convention, `--bmf-out` flag,
merge_bmf.py, expected slug set).

### Removed

- `benchmarks/bmf_adapter.py` — Python regex parser that converted
`bench_throughput` stdout text into BMF JSON. Replaced by native BMF
emission via `--bmf-out=`.
- `benchmarks/test_bmf_adapter.py` — unit tests for the parser.
Replaced by `benchmarks/tests/test_merge_bmf.py`.
- `benchmarks/nim/bench_main.nim` — aggregator binary that wrapped
bench_throughput + bench_latency and produced a custom JSON shape.
`bench_throughput` is now the canonical entry point.

## [4.1.0] - 2026-05-01

### Added
Expand Down
74 changes: 46 additions & 28 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,36 @@ regression gate via [Bencher.dev](https://bencher.dev).
## Structure

- `nim/` - Nim benchmarks (lockfreequeues, Loony, Nim channels)
- `nim/bench_common.nim` - Shared bench harness (BMF emission, stats,
Histogram with top-K + reservoir percentiles, throughput / latency
runners). One module, consumed by every per-topology bench binary.
- `nim/bench_throughput.nim` - Throughput driver. Emits Bencher Metric
Format JSON natively via `--bmf-out=<path>`.
- `nim/adapters/` - One file per upstream queue library
(`<library_slug>_adapter.nim`). Adapters expose a `push(value)
-> PushResult` / `pop() -> PopResult[T]` shape consumed by the
shared harness.
- `merge_bmf.py` - Stateless union of per-binary BMF JSON fragments
into a single `merged.json` for `bencher run`. Exits 1 on
`(slug, measure)` collisions naming the colliding inputs.
- `results/` - JSON output from local benchmark runs
- `runner.py` - Orchestrates local benchmark execution
- `bmf_adapter.py` - Converts `bench_throughput` stdout to Bencher Metric Format
- `test_bmf_adapter.py` - Unit tests for the BMF adapter

## Quick Start (local)

```bash
# Run all Nim throughput benchmarks (1M messages × 33 runs - takes a while).
# Run all Nim throughput benchmarks (1M messages x 33 runs - takes a while).
nim c -r -d:release -d:danger --threads:on benchmarks/nim/bench_throughput.nim

# Same, but the CI wall-clock budget (100k × 5).
# Same, but the CI wall-clock budget (100k x 5).
nim c -r -d:release -d:danger --threads:on \
-d:MessageCount=100000 -d:DefaultRuns=5 -d:WarmupRuns=2 \
-d:UnboundedMupsicRuns=5 \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BOT-A2 — Low (quality)
README documentation/CMD-config discrepancy: Quick Start section shows old 100K CI budget
The « Quick Start (local) » section shows -d:MessageCount=100000 and labels it « Same, but the CI wall-clock budget (100k × 5) ». The actual CI workflow in .github/workflows/bench.yml now uses -d:MessageCount=1000000 (1M) plus two new defines (-d:UnboundedMupsicRuns=3 -d:UnboundedMupsicMessageCount=500000) that the Quick Start section omits entirely. The « Cloud benchmarking (Bencher.dev) » section further down correctly reflects the 1M / 3-run CI shape. The inconsistent Quick Start block may lead devs to reproduce CI results with the wrong parameters.

benchmarks/nim/bench_throughput.nim

# Convert the captured stdout to BMF JSON for upload / inspection.
./.tmp/bench_throughput > bench_output.txt
python3 benchmarks/bmf_adapter.py bench_output.txt bench_results.json
# Emit BMF JSON natively (no Python parser; see merge step below).
./.tmp/bench_throughput --bmf-out=throughput.json
python3 benchmarks/merge_bmf.py merged.json throughput.json
```

## Metrics
Expand All @@ -40,13 +50,16 @@ python3 benchmarks/bmf_adapter.py bench_output.txt bench_results.json
for every PR and every push to `main`/`devel`. The workflow:

1. Compiles `bench_throughput` with the CI run shape
(`-d:MessageCount=100000 -d:DefaultRuns=5 -d:WarmupRuns=2
-d:UnboundedMupsicRuns=5`).
2. Captures stdout to `bench_output.txt`.
3. Runs `bmf_adapter.py` to emit `bench_results.json` in
[Bencher Metric Format](https://bencher.dev/docs/reference/bencher-metric-format/).
4. Uploads the JSON to the `lockfreequeues` Bencher project via the
`bencherdev/bencher@main` action.
(`-d:MessageCount=1000000 -d:DefaultRuns=5 -d:WarmupRuns=2
-d:UnboundedMupsicRuns=3 -d:UnboundedMupsicMessageCount=500000`).
2. Runs `bench_throughput --bmf-out=throughput.json`, which writes
Bencher Metric Format JSON natively.
3. Runs `python3 benchmarks/merge_bmf.py merged.json throughput.json`
to produce a single `merged.json` for upload. The merge step is a
no-op union today, but stays in place for the per-topology binary
split landing in PR 2-4.
4. Uploads `merged.json` to the `lockfreequeues` Bencher project via
the `bencherdev/bencher@main` action.

On pull requests, Bencher posts a comparison comment against the base
branch using `--start-point-clone-thresholds` and `--start-point-reset`,
Expand All @@ -64,35 +77,40 @@ The cloud workflow requires:
with write access to the project.

Until those exist the `bench` workflow will fail on the upload step;
PR / push events still produce the `bench_results.json` artifact in the
PR / push events still produce the `merged.json` artifact in the
job log so local debugging is possible without the upload.

### BMF schema emitted

```json
{
"<variant>/<P>p<C>c": {
"throughput": {
"<library_slug>/<topology>/<P>p<C>c": {
"throughput_ops_ms": {
"value": <mean ops/ms>,
"lower_value": <min ops/ms, optional>,
"upper_value": <max ops/ms, optional>
"lower_value": <mean - stddev>,
"upper_value": <mean + stddev>
}
}
}
```

`<variant>` is one of `sipsic`, `mupmuc`, `unbounded_mupsic`, `channels`.
`lower_value` / `upper_value` are populated only for blocks that print a
`min: ... max: ...` line (currently only the `unbounded_mupsic` group).
Non-finite samples (`inf`, `nan`) are dropped with a stderr warning so
spurious cold-cache outliers do not poison the upload.
Slugs are alpha-sorted at the top level and measures are alpha-sorted
within each slug. `lower_value` / `upper_value` are omitted when the
emitter receives `NaN` sentinels for the bounds. Current slug set
emitted by `bench_throughput`:

## Running adapter tests
- `lockfreequeues_sipsic/spsc/1p1c`
- `lockfreequeues_mupmuc/mpmc/{1,2,4,8}p{1,2,4,8}c`
- `lockfreequeues_unbounded_mupsic/mpsc_unbounded/{1,2,4}p1c`
- `nim_channels/mpmc/{1,2,4}p{1,2,4}c`

## Running merge_bmf tests

```bash
python3 benchmarks/test_bmf_adapter.py -v
python3 -m unittest benchmarks.tests.test_merge_bmf -v
```

The tests use only the Python standard library (`unittest`) and run in
< 0.1s. They cover full and partial bench output, unknown variants, and
the CLI's exit codes.
< 0.1s. They cover slug regex enforcement, measure regex enforcement,
collision detection (with both colliding files named in stderr), and
alpha-sorted output.
Loading
Loading