roadmap: ship v0.9, promote Process groups to up-next
- Compact v0.9 (Wake-path latency) into Shipped; record the RFC 004 spinning excision as a deviation (preserved on branch rfc-004-spinning). - Drop the v0.7 entry, folding its reference into the Shipped history pointer. - Promote Process groups from Later into the up-next slot v0.9 vacated; Per-switch cost now leads Later/Highest priority.
This commit is contained in:
+21
-59
@@ -2,20 +2,9 @@
|
|||||||
|
|
||||||
## Shipped (compacted — full cycle plans and deviation records live in git history)
|
## Shipped (compacted — full cycle plans and deviation records live in git history)
|
||||||
|
|
||||||
Cycles before v0.7 (v0.4 actor primitives, v0.5 runtime decomposition &
|
Cycles before v0.8 (v0.4 actor primitives, v0.5 runtime decomposition &
|
||||||
pluggable run queue, v0.6 actor ergonomics): see `git log ROADMAP.md`.
|
pluggable run queue, v0.6 actor ergonomics, v0.7 select on epoch-stamped
|
||||||
|
consuming wakes): see `git log ROADMAP.md`.
|
||||||
### v0.7 — select, on epoch-stamped consuming wakes ✅
|
|
||||||
A 24-bit park-epoch packed into the slot word gives every wait an identity:
|
|
||||||
registrations carry `(pid, epoch)`, every successful wake **consumes** the
|
|
||||||
epoch, stale wakes die at one failed CAS. Subsumed the per-primitive wait
|
|
||||||
seqs (channel, mutex, timer); the only wildcard wake left is `request_stop`
|
|
||||||
(terminal). On top: `select`/`select_timeout` — ready-index wait over many
|
|
||||||
receivers, priority order, no cancellation pass. Loom theorems re-proved on
|
|
||||||
the new word. Notable deviations: `retire_wait` for the no-park exit path
|
|
||||||
(plan missed it); the Drop-guard deregistration was superseded by relaxed
|
|
||||||
single-receiver asserts; a closed arm is ready *forever* (documented gotcha).
|
|
||||||
Commits `4913835`…`00128f3`, `a0a93b6`.
|
|
||||||
|
|
||||||
### v0.8 — gen_server: handle_info / handle_down + io fd hygiene ✅
|
### v0.8 — gen_server: handle_info / handle_down + io fd hygiene ✅
|
||||||
Spent `select` on the server loop: static info arms (`type Info`,
|
Spent `select` on the server loop: static info arms (`type Info`,
|
||||||
@@ -27,6 +16,19 @@ entry permanently poisoned the fd). Deviation: the plain-inbox fast path
|
|||||||
narrowed; servers holding a `Watcher` select forever.
|
narrowed; servers holding a `Watcher` select forever.
|
||||||
Commits `e5d1b3b`, `24b95c9`, `f6969e5`.
|
Commits `e5d1b3b`, `24b95c9`, `f6969e5`.
|
||||||
|
|
||||||
|
### v0.9 — Wake-path latency ✅
|
||||||
|
Attacked per-wake latency with the RFC 005 **wake slot**: a per-scheduler,
|
||||||
|
thread-local, capacity-one wake cache checked before the shared queue, pushed
|
||||||
|
only from actor context, slot-then-shared pop with the waker's residual slice
|
||||||
|
as the starvation bound (`slot_hits`/`slot_displacements`). Benched via the
|
||||||
|
slot on/off dimension of `rq_runtime` — ping-pong-pairs (win), yield-storm
|
||||||
|
(regression guard), spawn-storm (neutrality); results annotated in RFC 005.
|
||||||
|
The RFC 004 spinning-workers experiment, originally scoped here, was evaluated
|
||||||
|
and **excised** (not worth the code cost; preserved on branch
|
||||||
|
`rfc-004-spinning`). Also a false-sharing fix (`align(64)` on `SchedulerStats`)
|
||||||
|
and a termination wake for idle siblings.
|
||||||
|
Commits `2708042`, `37d9319`, `eddf3fe`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Decision record — queue topology 🔒 CLOSED (2026-06-10)
|
## Decision record — queue topology 🔒 CLOSED (2026-06-10)
|
||||||
@@ -48,51 +50,7 @@ Consequences:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v0.9 — Wake-path latency
|
## Up next — Process groups: the primitive pubsub & channels should have sat on
|
||||||
|
|
||||||
Goal: attack per-wake latency — the measured ceiling — with the two specified
|
|
||||||
mechanisms, each benched against a clean baseline before the next lands.
|
|
||||||
Order: RFC 005 first (the slot is measured against the just-benched idle
|
|
||||||
policy), then RFC 004 (measured against the slot-accepted baseline), then an
|
|
||||||
interaction pass.
|
|
||||||
|
|
||||||
Full specs: `rfc_005-wake-slot.md`, `rfc_004-tunable-scheduler-idle-policy.md`
|
|
||||||
(artefacts.kalsbeek.dev).
|
|
||||||
|
|
||||||
### 1. Wake slot (RFC 005)
|
|
||||||
Per-scheduler, thread-local, capacity-one wake cache, checked before the
|
|
||||||
shared queue. Runtime-selected via `Config { wake_slot: bool }`, default off
|
|
||||||
until accepted (one binary benches both arms).
|
|
||||||
- **Push policy:** slot-eligible iff the wake originates from actor context
|
|
||||||
(`current_pid().is_some()`). Scheduler-context wakes (timer/IO drain) and
|
|
||||||
spawns always go shared.
|
|
||||||
- **Displacement:** newest wake takes the slot, occupant pushed shared (Go
|
|
||||||
semantics); the branch swap is the fallback if benches look pathological.
|
|
||||||
- **Pop order:** slot, then shared. Slot-popped actors inherit the waker's
|
|
||||||
remaining timeslice — a handoff chain is bounded by one slice, so the
|
|
||||||
shared queue is consulted at least once per slice per scheduler (the
|
|
||||||
starvation bound, zero new counters).
|
|
||||||
- **Invariant care:** the slot push replaces `run_queue.push` at the tail of
|
|
||||||
the `Parked → Queued` CAS; at-most-once-enqueued holds verbatim (pid in
|
|
||||||
slot ⊕ shared queue). Stall blast radius grows by exactly one actor.
|
|
||||||
- Counters: `slot_hits`, `slot_displacements`.
|
|
||||||
|
|
||||||
### 2. Slot shootout
|
|
||||||
Extend `rq_runtime`/`bench_rq.sh` with the slot on/off dimension (cheap —
|
|
||||||
it's a Config knob, not a feature rebuild). Same sweep as the rq shootout.
|
|
||||||
- **ping-pong-pairs** — target metric; expect the win, single- and
|
|
||||||
multi-scheduler.
|
|
||||||
- **yield-storm** — regression guard; yields never touch the slot, any delta
|
|
||||||
is pop-path overhead.
|
|
||||||
- **spawn-storm** — neutrality check; spawns bypass the slot by policy.
|
|
||||||
Acceptance flips the default on and re-baselines for RFC 004.
|
|
||||||
This is done, see results annotated in RFC005.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Later
|
|
||||||
### Highest priority
|
|
||||||
#### Process groups: the primitive pubsub & channels should have sat on
|
|
||||||
Context: urus is a webserver written on top of smarm to provide a testing target.
|
Context: urus is a webserver written on top of smarm to provide a testing target.
|
||||||
A named pid→multiset map with monitor-backed removal: `registry.rs` generalised
|
A named pid→multiset map with monitor-backed removal: `registry.rs` generalised
|
||||||
from name↔pid *bimap* to name→*multiset*, the death hook reused verbatim. Local
|
from name↔pid *bimap* to name→*multiset*, the death hook reused verbatim. Local
|
||||||
@@ -103,6 +61,10 @@ member), with different netsplit consequences. Urus shipped pubsub/channels pred
|
|||||||
and want reframing on top of it. Foundational, so early in the post-v0.9 stack.
|
and want reframing on top of it. Foundational, so early in the post-v0.9 stack.
|
||||||
Needs an RFC.
|
Needs an RFC.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Later
|
||||||
|
### Highest priority
|
||||||
#### Per-switch cost (context shims, epoch protocol)
|
#### Per-switch cost (context shims, epoch protocol)
|
||||||
The shootout's residual: per-wake latency is 0.16–0.18 µs at N=1 and
|
The shootout's residual: per-wake latency is 0.16–0.18 µs at N=1 and
|
||||||
0.8–1.2 µs at N=8+, dominated by the context-switch shims and the epoch
|
0.8–1.2 µs at N=8+, dominated by the context-switch shims and the epoch
|
||||||
|
|||||||
Reference in New Issue
Block a user