diff --git a/examples/causal_attrib_probe.rs b/examples/causal_attrib_probe.rs index 2663669..537831d 100644 --- a/examples/causal_attrib_probe.rs +++ b/examples/causal_attrib_probe.rs @@ -1,12 +1,15 @@ //! Attribution-efficiency probe (RFC 007 follow-up). //! -//! Hypothesis: the ~4pt impact shortfall on the 24-core validation -//! (+29.3/+83.5 vs theoretical +33/+100) is a constant attribution -//! efficiency eff ≈ 0.91 — only ~91% of true in-target-site time is ever -//! sampled, because samples are taken only when `maybe_preempt`'s cold -//! block fires while in-site: the tail between the last in-site check and -//! `SiteGuard` drop (and any in-site time truncated by a deschedule) is -//! discarded. +//! Original hypothesis: the ~4pt impact shortfall on the 24-core +//! validation (+29.3/+83.5 vs theoretical +33/+100) is a constant +//! attribution efficiency eff ≈ 0.91 from site-exit tail truncation. +//! The guard-drop flush closed that leak, yet eff held at ~0.93 — +//! RESOLVED (2026-07-13 sweep): the residual is runnable off-CPU time +//! inside the site (~4.9 slice-expiry yields/entry x ~5.6µs runqueue +//! wait), wall time the ground truth below counts but on-CPU +//! attribution correctly skips. The offcpu audit bucket now counts it; +//! `eff+offcpu` printed per window should sit at ~1.00 — the +//! closed-books check. //! //! Measurement: same pipeline as `causal_pipeline`, but the `reserve` //! actor also measures its raw in-site time directly (rdtsc at guard @@ -19,8 +22,9 @@ //! //! tail_us/entry = (Δin_site − Δglobal_delay/(pct/100)) / Δentries //! -//! A constant eff across 25/50% with tail/entry in the tens of µs (about -//! half the causal-check cadence) confirms site-exit tail truncation. +//! A constant eff across 25/50% with tail/entry in the tens of µs +//! localizes a per-entry mechanism; `eff+offcpu` ≈ 1.00 confirms the +//! runnable-gap account and rules out any remaining silent loss. //! //! Run: cargo run --release --example causal_attrib_probe --features smarm-causal @@ -147,6 +151,11 @@ fn main() { let attributed = injected as f64 / (pct as f64 / 100.0); let eff = attributed / in_site as f64; + // Books-closure check: add back the runnable off-CPU gaps the + // audit counted (delta terms -> raw via /pct) — should be ~1.00. + let eff_closed = (injected as f64 + audit.offcpu_in_site_cycles as f64) + / (pct as f64 / 100.0) + / in_site as f64; let missing = in_site as f64 - attributed; let tail_us = if entries > 0 { missing / entries as f64 / hz * 1e6 @@ -154,7 +163,7 @@ fn main() { f64::NAN }; println!( - "pct {pct:>2}% in_site {:>8.1}ms attributed {:>8.1}ms eff {eff:.3} entries {entries} missing/entry {tail_us:.1}µs", + "pct {pct:>2}% in_site {:>8.1}ms attributed {:>8.1}ms eff {eff:.3} eff+offcpu {eff_closed:.3} entries {entries} missing/entry {tail_us:.1}µs", in_site as f64 / hz * 1e3, attributed / hz * 1e3, ); @@ -163,13 +172,15 @@ fn main() { // missing attribution above. let ms = |c: u64| c as f64 / hz * 1e3; println!( - " audit: absorbed {:>7.1}ms forgiven {:>6.1}ms drop park {:>5.2}ms/{:<5} yield {:>5.2}ms/{:<5} discard >max {:>5.2}ms/{:<3} unarmed {}", + " audit: absorbed {:>7.1}ms forgiven {:>6.1}ms drop park {:>5.2}ms/{:<5} yield {:>5.2}ms/{:<5} offcpu {:>6.2}ms/{:<5} discard >max {:>5.2}ms/{:<3} unarmed {}", ms(audit.spin_absorbed_cycles), ms(audit.park_forgiven_cycles), ms(audit.drop_park_cycles), audit.drop_park_n, ms(audit.drop_yield_cycles), audit.drop_yield_n, + ms(audit.offcpu_in_site_cycles), + audit.offcpu_in_site_n, ms(audit.discard_overmax_cycles), audit.discard_overmax_n, audit.discard_unarmed_n