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:
@@ -902,10 +902,78 @@ fn ledger_audit_renders_per_cell() {
|
||||
discard_overmax_cycles: 5_000,
|
||||
discard_overmax_n: 1,
|
||||
discard_unarmed_n: 0,
|
||||
offcpu_in_site_cycles: 20_000,
|
||||
offcpu_in_site_n: 3,
|
||||
}];
|
||||
let s = render_ledger_audit(&results);
|
||||
assert!(s.contains("ledger audit"), "missing header: {s}");
|
||||
assert!(s.contains("audit-render-site"), "missing site line: {s}");
|
||||
assert!(s.contains("absorbed"), "missing absorbed column: {s}");
|
||||
assert!(s.contains("forgiven"), "missing forgiven column: {s}");
|
||||
assert!(s.contains("offcpu"), "missing offcpu column: {s}");
|
||||
}
|
||||
|
||||
/// A runnable (yield) deschedule inside the target site opens an off-CPU
|
||||
/// gap; the resume closing it lands in the same experiment window, so the
|
||||
/// audit counts it. RFC 007: this is the located @50 deficit — runnable
|
||||
/// queue-wait is real wall time in-site that on-CPU attribution correctly
|
||||
/// skips; the bucket exists so the books close instead of leaving a
|
||||
/// silent ~7% residual.
|
||||
#[test]
|
||||
fn audit_counts_offcpu_runnable_gap_in_target_site() {
|
||||
let _s = serial();
|
||||
smarm::init(smarm::Config::exact(1)).run(|| {
|
||||
smarm::causal::begin_experiment_for_test("audit-offcpu-site", 50);
|
||||
let c0 = smarm::causal::ledger_counters();
|
||||
let h = smarm::spawn(|| {
|
||||
let _g = smarm::causal_site!("audit-offcpu-site");
|
||||
smarm::check!(); // arm
|
||||
smarm::yield_now(); // runnable gap: deschedule -> resume
|
||||
smarm::check!();
|
||||
});
|
||||
h.join().unwrap();
|
||||
let c1 = smarm::causal::ledger_counters();
|
||||
smarm::causal::end_experiment_for_test();
|
||||
|
||||
assert!(
|
||||
c1.offcpu_in_site_n > c0.offcpu_in_site_n,
|
||||
"in-site runnable gap recorded no offcpu event"
|
||||
);
|
||||
assert!(
|
||||
c1.offcpu_in_site_cycles > c0.offcpu_in_site_cycles,
|
||||
"in-site runnable gap carried no cycles"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// An off-CPU gap that straddles `end()` — even into a later window with
|
||||
/// an identical site+pct word (live in the attrib probe's 50,50 schedule)
|
||||
/// — is dropped, not counted: the stash carries the experiment *epoch*,
|
||||
/// so a straddling resume can never leak a cooldown across windows.
|
||||
#[test]
|
||||
fn audit_offcpu_gap_across_windows_not_counted() {
|
||||
let _s = serial();
|
||||
smarm::init(smarm::Config::exact(1)).run(|| {
|
||||
smarm::causal::begin_experiment_for_test("audit-offcpu-straddle", 50);
|
||||
let c0 = smarm::causal::ledger_counters();
|
||||
let h = smarm::spawn(|| {
|
||||
let _g = smarm::causal_site!("audit-offcpu-straddle");
|
||||
smarm::check!();
|
||||
smarm::yield_now(); // stash carries window A's epoch
|
||||
smarm::check!();
|
||||
});
|
||||
// FIFO on exact(1): let h run to its yield, then close window A and
|
||||
// open a same-word window B before h resumes.
|
||||
smarm::yield_now();
|
||||
smarm::causal::end_experiment_for_test();
|
||||
smarm::causal::begin_experiment_for_test("audit-offcpu-straddle", 50);
|
||||
h.join().unwrap();
|
||||
let c1 = smarm::causal::ledger_counters();
|
||||
smarm::causal::end_experiment_for_test();
|
||||
|
||||
assert_eq!(
|
||||
c1.offcpu_in_site_n, c0.offcpu_in_site_n,
|
||||
"gap straddling windows must not be counted"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user