feat(scheduler): RFC 005 wake slot — per-scheduler capacity-one wake cache

A thread-local Cell<Option<Pid>> per scheduler, checked before the shared
run queue. Runtime-selected via Config { wake_slot: bool }, default OFF
until the slot shootout accepts it (one binary benches both arms).

Push policy: slot-eligible iff the wake originates from actor context
(current_pid().is_some()) — the slot push replaces run_queue.push at the
tail of the unpark protocol's Parked → Queued CAS, so at-most-once-enqueued
holds verbatim as (slot ⊕ shared queue). Scheduler-context wakes (timer/IO
drain) and spawns always go shared (spawns never reach unpark_inner at all).
Displacement is Go semantics: newest wake takes the slot, occupant pushed
shared — moved, never copied.

Pop order: slot, then shared. Slot-popped actors skip reset_timeslice() and
inherit the waker's remaining slice; a handoff chain is bounded by one
slice, after which the preempt-yield re-enqueue goes shared (a yield is not
a wake) — the one-slice starvation bound, zero new counters. Idle and
AllDone are only reachable with an empty local slot by pop order; an
occupied slot elsewhere holds a Queued (live) actor, so the counter-first
termination argument is untouched.

Observability: per-thread slot_hits / slot_displacements (reset at run()
start so post-run stats() reads are per-run), SlotPush/SlotPop trace events.

Bench plan (roadmap v0.9 item 2): rq_runtime gains the slot on/off
dimension (SMARM_BENCH_SLOT, default "0 1") — ping-pong-pairs is the
target metric, yield-storm the regression guard, spawn-storm the
neutrality check. RQCSV grows a slot column; RQSLOT lines carry the
counters; bench_rq.sh aggregates both. Tests pin the push policy through
the counters (actor-context hits, spawn/join bypass, displacement,
default-off, per-run reset).
This commit is contained in:
Claude
2026-06-11 20:20:07 +02:00
parent f09e992f32
commit 2708042990
6 changed files with 519 additions and 116 deletions
+22 -7
View File
@@ -1,31 +1,46 @@
#!/usr/bin/env bash
# Run-queue shootout driver (ROADMAP_v0.5 phase 4).
# Run-queue shootout driver (ROADMAP_v0.5 phase 4; RFC 005 slot dimension
# added for the v0.9 slot shootout).
#
# Rebuilds the runtime bench once per rq-* feature and runs the raw-structure
# microbench once (it covers all structures in a single binary). Results land
# in bench_results/ as full logs; the RQCSV lines are aggregated into
# bench_results/summary.csv for plotting.
# microbench once (it covers all structures in a single binary). The RFC 005
# wake slot is a runtime Config knob, NOT a feature — each rq_runtime binary
# sweeps slot off/on internally (SMARM_BENCH_SLOT, default "0 1"). Results
# land in bench_results/ as full logs; the RQCSV lines are aggregated into
# bench_results/summary.csv and the RQSLOT counter lines (slot hits /
# displacements, slot-on configs only) into bench_results/slot_counters.csv.
#
# Tune the sweep for the box, e.g. on the 20-core machine:
# SMARM_BENCH_THREADS="1 2 4 8 16 20" ./scripts/bench_rq.sh
# Slot-only re-run against the frozen rq-mutex substrate:
# SMARM_BENCH_SLOT="0 1" cargo bench --bench rq_runtime
set -euo pipefail
cd "$(dirname "$0")/.."
OUT=bench_results
mkdir -p "$OUT"
: "${SMARM_BENCH_THREADS:=1 2 4}"
export SMARM_BENCH_THREADS
: "${SMARM_BENCH_SLOT:=0 1}"
export SMARM_BENCH_THREADS SMARM_BENCH_SLOT
echo "== raw structures (one binary, all variants) =="
cargo bench --bench rq_micro 2>&1 | tee "$OUT/micro.txt"
for v in rq-mutex rq-mpmc rq-striped; do
echo "== runtime benches: $v =="
echo "== runtime benches: $v (slot sweep: $SMARM_BENCH_SLOT) =="
cargo bench --bench rq_runtime --no-default-features --features "$v" \
2>&1 | tee "$OUT/runtime-$v.txt"
done
echo "bench,kind,a,b,c,d,median_us,ops_per_s" > "$OUT/summary.csv"
# runtime rows: kind,variant,slot,bench,threads,work,median_us,ops_per_s
# micro rows: kind,structure,threads,p:c,items,median_us,items_per_s (one
# column narrower, as before — split on kind when plotting)
echo "kind,a,b,c,d,e,median_us,ops_per_s" > "$OUT/summary.csv"
grep -h '^RQCSV,' "$OUT"/*.txt | sed 's/^RQCSV,//' >> "$OUT/summary.csv"
echo "variant,bench,threads,slot_hits,slot_displacements" > "$OUT/slot_counters.csv"
grep -h '^RQSLOT,' "$OUT"/*.txt | sed 's/^RQSLOT,//' >> "$OUT/slot_counters.csv" || true
echo
echo "Summary: $OUT/summary.csv ($(($(wc -l < "$OUT/summary.csv") - 1)) rows)"
echo "Slot counters: $OUT/slot_counters.csv ($(($(wc -l < "$OUT/slot_counters.csv") - 1)) rows)"