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:
@@ -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))]
|
||||
|
||||
Reference in New Issue
Block a user