feat(bench): phase 4 — run-queue bench harness + shootout driver

- benches/rq_micro.rs: raw-structure microbench, threads x producer:consumer
  ratio sweep. Benches all three queue types in one binary (they compile in
  every build; only the runtime alias is feature-selected), so no rebuild
  dance. Queues sized to the op count so the occupancy contract is met.
- benches/rq_runtime.rs: whole-runtime benches with the selected variant:
  yield-storm (pure queue churn), ping-pong-pairs (park/unpark latency),
  spawn-storm (slab + free list + queue under churn). Scheduler-count sweep.
- scripts/bench_rq.sh: rebuilds rq_runtime per rq-* feature, runs rq_micro
  once, aggregates RQCSV lines into bench_results/summary.csv.
- All knobs via SMARM_BENCH_* env vars; house table format + machine lines.
- run_queue module is now #[doc(hidden)] pub (types + push/pop/len +
  MpmcRing::with_capacity) solely so the external bench binary can drive the
  raw structures.

docs(roadmap): phase 4 ticked (harness done; numbers from the 20-core box).
New fast-follow per review: assert the invariants we lean on — debug_assert!
on hot paths, loud assert!/panic on cold ones, at the point of reliance;
sweep existing code during the phase-5 audit, adopt as house style.

Validated end-to-end at smoke scale on the 1-core sandbox: full driver run,
24-row summary.csv across micro (3 structures x sweeps) and runtime
(3 variants x 3 benches x thread sweep).
This commit is contained in:
Claude
2026-06-09 20:44:10 +00:00
parent 1b3b618aa7
commit 6d9f3698d4
8 changed files with 456 additions and 25 deletions
+22 -8
View File
@@ -86,14 +86,17 @@ Make slot lookup lock-free and per-slot state independently mutable.
(`live == 0` alone implies the queue holds nothing actionable; argument
rewritten at the `schedule_loop` site).
## Phase 4 — Bench harness
- [ ] Raw-structure microbench: N threads, push/pop throughput vs thread count,
sweeping producer:consumer ratios. Isolates the data structure.
- [ ] Runtime-level: yield-storm, ping-pong-pairs, spawn-storm, all sweeping
scheduler count. Reuses existing `benches/` harness style.
- [ ] Driver script rebuilding per `rq-*` feature to compare variants in one go.
- [ ] Sandbox validates correctness via oversubscribed `Config::exact(N)` on 1
core; real contention numbers come from the 20-core box.
## Phase 4 — Bench harness ✅ DONE (harness; real numbers from the 20-core box)
- [x] `benches/rq_micro.rs`: raw structures, threads × p:c ratio sweep. One
binary covers all three structures (types compile in every build).
- [x] `benches/rq_runtime.rs`: yield-storm, ping-pong-pairs, spawn-storm,
sweeping scheduler count; variant baked in by feature.
- [x] `scripts/bench_rq.sh`: rebuilds per `rq-*` feature, aggregates RQCSV
lines into `bench_results/summary.csv`. Knobs via SMARM_BENCH_* env;
e.g. `SMARM_BENCH_THREADS="1 2 4 8 16 20" ./scripts/bench_rq.sh`.
- [x] Harness validated end-to-end at smoke scale on the 1-core sandbox;
contention curves and the actual variant decision come from the
20-core box.
## Phase 5 — Safety hardening & model checking
- [ ] `loom` (dev-dependency only, x86 Linux) model tests for the slot state
@@ -104,6 +107,17 @@ Make slot lookup lock-free and per-slot state independently mutable.
---
## Fast follow (post-v0.5, written down so it isn't lost)
- **Assert the invariants we lean on.** This cycle accumulated load-bearing
invariants: at-most-once-enqueued, queue-ops-under-NoPreempt, never two
cold locks, cold-path generation re-verify under the lock, finalize's
decrement-last, pushes-pair-with-Queued-transitions, thread-local guards
never crossing a switch point. Whenever code RELIES on one and a cheap
check exists, assert it at the point of reliance — `debug_assert!` on hot
paths, full `assert!`/loud panic on cold ones — so a violation fails at
the breakage site, not three modules downstream (the slab-overflow panic
and the queue-op preemption debug_assert are the pattern). Sweep the
existing code for missed spots; new code adopts it as house style. The
phase-5 audit is the natural vehicle for the sweep.
- **Channel mutex migration.** `channel::Inner<T>` is `Arc<Mutex<_>>` of the
same poison class as the old shared lock; `recv_match` even runs a user
predicate under it. The Phase-1 `check_cancelled` gating already removes the