feat(causal): offcpu audit bucket — the @50 deficit located (RFC 007)

GPU sweep decomposed the ~23ms/700ms @50 injection deficit: every
ledger bucket is ~zero (drop park 0, discards 0, drop yield ~0.3ms),
books balance at absorbed+forgiven = 4x injected in all 48 cells, and
the 0%-cell contamination signature is absent. The probe pins the
residual: eff 0.933-0.943, a constant 22-27µs missing per site entry
= ~4.9 slice-expiry yields/entry x ~5.6µs runqueue wait. The
"deficit" is runnable off-CPU time inside the site — wall time the
probe's ground truth counts but on-CPU attribution correctly skips
(Coz model: speeding the site's code does not shrink queue-wait).

Measure-only reclassification, no behaviour change: a yield in the
target site stashes (tsc, experiment epoch) on the slot; the next
on_resume counts the gap into OFFCPU_IN_SITE_{CYCLES,N} (would-be
delta terms, MAX_SAMPLE_CYCLES-capped) iff the epoch still matches
and the word is live — a gap straddling end()/a same-word begin()
(live in the probe's 50,50 schedule) is dropped, never a leaked
cooldown. Parks excluded: blocked time is forgiveness territory.
New offcpu column in render_ledger_audit; LedgerCounters and
ExperimentResult grow the two fields. Fidelity footer now states
the on-CPU basis (deliberate wording change to the pinned summary;
the substring pin test still holds). +2 tests (counted gap; epoch
straddle) + render assert.
This commit is contained in:
Claude (sandbox)
2026-07-13 12:46:15 +00:00
parent 9bfeb2c6a2
commit 0ee3fe7330
3 changed files with 169 additions and 6 deletions
+34
View File
@@ -467,6 +467,18 @@ pub(crate) struct Slot {
/// history. Written by the owning scheduler thread at the deschedule /
/// resume boundary only — same single-writer discipline as `causal_delay`.
causal_parked: AtomicBool,
/// RFC 007 — TSC at this actor's last *runnable* (yield) deschedule
/// while it sat in the live experiment's target site; 0 = no gap
/// pending. Paired with `causal_desched_epoch`; written on the
/// deschedule path, consumed at the next resume — same single-writer
/// discipline as `causal_delay`.
causal_desched_tsc: AtomicU64,
/// RFC 007 — experiment epoch live at that deschedule (see
/// `causal::EXPERIMENT_EPOCH`): the resume counts the gap only into
/// the same window, so one straddling `end()` — or a later `begin()`
/// with an identical site+pct word — is dropped instead of leaking a
/// cooldown across windows.
causal_desched_epoch: AtomicU64,
/// Cold lifecycle data. See [`SlotCold`].
pub(crate) cold: RawMutex<SlotCold>,
}
@@ -484,6 +496,8 @@ impl Slot {
causal_site: AtomicU32::new(0),
causal_delay: AtomicU64::new(0),
causal_parked: AtomicBool::new(true),
causal_desched_tsc: AtomicU64::new(0),
causal_desched_epoch: AtomicU64::new(0),
cold: RawMutex::new(SlotCold {
actor: None,
waiters: Vec::new(),
@@ -577,6 +591,8 @@ impl Slot {
// True, not false: the first resume of a fresh actor is a "wake" —
// it must not owe the process's accumulated delay history.
self.causal_parked.store(true, Ordering::Relaxed);
self.causal_desched_tsc.store(0, Ordering::Relaxed);
self.causal_desched_epoch.store(0, Ordering::Relaxed);
}
/// RFC 007 — mark that this actor's deschedule was a genuine park.
@@ -597,6 +613,24 @@ impl Slot {
self.causal_parked.swap(false, Ordering::Relaxed)
}
/// RFC 007 — stash the runnable-deschedule instant and the experiment
/// epoch it happened under (offcpu-gap audit; see `causal::on_resume`).
#[cfg_attr(not(feature = "smarm-causal"), allow(dead_code))]
#[inline]
pub(crate) fn set_causal_desched(&self, tsc: u64, epoch: u64) {
self.causal_desched_tsc.store(tsc, Ordering::Relaxed);
self.causal_desched_epoch.store(epoch, Ordering::Relaxed);
}
/// RFC 007 — consume the stash: `(tsc, epoch)`; `(0, _)` = none pending.
#[cfg_attr(not(feature = "smarm-causal"), allow(dead_code))]
#[inline]
pub(crate) fn take_causal_desched(&self) -> (u64, u64) {
let tsc = self.causal_desched_tsc.swap(0, Ordering::Relaxed);
let epoch = self.causal_desched_epoch.load(Ordering::Relaxed);
(tsc, epoch)
}
/// RFC 007 — current causal site id (0 = none). Single-writer: only the
/// on-CPU actor's thread writes, via the site-guard enter/exit.
#[cfg_attr(not(feature = "smarm-causal"), allow(dead_code))]