Files
smarm/examples/causal_attrib_probe.rs
T
Claude (sandbox) d496914d40 fix(causal): flush target-site samples at guard boundaries (RFC 007)
Samples were taken only when maybe_preempt's cold block happened to fire
in-site, so the interval between the last check and SiteGuard drop was
discarded on every site entry. Measured live on the 24-core box:
22-29us lost per entry, a constant attribution efficiency of ~0.93-0.94,
which under-reported every impact (+83.5% where theory says +100%; the
observed shortfall fits 1/(1-pct*eff)-1 at both 25% and 50%).

SiteGuard enter/drop now call site_transition(): leaving the target site
flushes the pending interval into the ledger (sample-only, never spins,
so safe under no-preempt regions); entering the target site re-arms the
sample clock so pre-site time is never attributed (the symmetric
over-attribution). Winner attribution is factored into attribute(),
shared by the cold check and the flush, with the same interval clamps.

Adds examples/causal_attrib_probe.rs (ground-truth in-site time vs
ledger attribution, the probe that confirmed the leak) and the
site_boundaries_flush_tail regression test (a site entry that never
hits a cold check must still be attributed). Also gates causal_probe
on smarm-causal in Cargo.toml - it never was, so featureless builds
of the examples were broken.
2026-07-12 19:35:06 +00:00

170 lines
6.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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.
//!
//! Measurement: same pipeline as `causal_pipeline`, but the `reserve`
//! actor also measures its raw in-site time directly (rdtsc at guard
//! enter/exit) and counts site entries. For each experiment window at
//! pct%:
//!
//! eff = (Δglobal_delay / (pct/100)) / Δin_site_cycles
//!
//! and the missing time per site entry localizes the leak:
//!
//! 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.
//!
//! Run: cargo run --release --example causal_attrib_probe --features smarm-causal
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
fn rdtsc() -> u64 {
// x86_64 only — same clock the ledger uses.
unsafe { core::arch::x86_64::_rdtsc() }
}
/// Same fixed-work loop as causal_pipeline (dependent LCG, preemptible).
fn work_iters(iters: u64) {
let mut acc = 0x2545_f491_4f6c_dd1du64;
let mut i = 0u64;
while i < iters {
let chunk_end = (i + 256).min(iters);
while i < chunk_end {
acc = acc.wrapping_mul(6364136223846793005).wrapping_add(i);
i += 1;
}
std::hint::black_box(acc);
smarm::check!();
}
}
fn calibrate_iters_per_us() -> u64 {
let n = 8_000_000u64;
let t = Instant::now();
work_iters(n);
(n / (t.elapsed().as_micros().max(1) as u64)).max(1)
}
static IN_SITE_CYCLES: AtomicU64 = AtomicU64::new(0);
static SITE_ENTRIES: AtomicU64 = AtomicU64::new(0);
fn main() {
let per_us = calibrate_iters_per_us();
println!("calibration: {per_us} work iters/µs");
let work_us = move |us: u64| work_iters(us * per_us);
let cores = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
println!("cores: {cores}");
if cores < 4 {
println!("probe: SKIPPED (needs the stages in parallel)");
return;
}
smarm::init(smarm::Config::default()).run(move || {
let stop = Arc::new(AtomicBool::new(false));
let (tx_ab, rx_ab) = smarm::channel::<u64>();
let (tx_bc, rx_bc) = smarm::channel::<u64>();
let stop_p = stop.clone();
let producer = smarm::spawn(move || {
let mut i = 0u64;
while !stop_p.load(Ordering::Relaxed) {
{
let _g = smarm::causal_site!("serialize");
work_us(200);
}
if tx_ab.send(i).is_err() {
break;
}
i += 1;
}
});
// Reserve: the target — instrumented with ground-truth in-site time.
let reserve = smarm::spawn(move || {
while let Ok(item) = rx_ab.recv() {
{
let t0 = rdtsc();
let _g = smarm::causal_site!("reserve");
work_us(400);
// Measured before guard drop: exactly the span the
// ledger should be attributing.
IN_SITE_CYCLES.fetch_add(rdtsc().saturating_sub(t0), Ordering::Relaxed);
SITE_ENTRIES.fetch_add(1, Ordering::Relaxed);
}
if tx_bc.send(item).is_err() {
break;
}
}
});
let notify = smarm::spawn(move || {
while rx_bc.recv().is_ok() {
{
let _g = smarm::causal_site!("notify");
work_us(50);
}
smarm::progress!("orders-processed");
}
});
let stop_bg = stop.clone();
let background = smarm::spawn(move || {
while !stop_bg.load(Ordering::Relaxed) {
let _g = smarm::causal_site!("background-compaction");
work_us(500);
}
});
smarm::sleep(Duration::from_millis(300));
let hz = smarm::causal::tsc_hz();
println!("tsc_hz: {:.3} GHz", hz / 1e9);
// Manual windows so ledger/ground-truth snapshots align exactly.
for &pct in &[25u32, 50, 50, 25] {
let g0 = smarm::causal::global_delay_cycles();
let s0 = IN_SITE_CYCLES.load(Ordering::Relaxed);
let e0 = SITE_ENTRIES.load(Ordering::Relaxed);
smarm::causal::begin_experiment_for_test("reserve", pct);
smarm::sleep(Duration::from_millis(1000));
smarm::causal::end_experiment_for_test();
let injected = smarm::causal::global_delay_cycles() - g0;
let in_site = IN_SITE_CYCLES.load(Ordering::Relaxed) - s0;
let entries = SITE_ENTRIES.load(Ordering::Relaxed) - e0;
let attributed = injected as f64 / (pct as f64 / 100.0);
let eff = attributed / in_site as f64;
let missing = in_site as f64 - attributed;
let tail_us = if entries > 0 {
missing / entries as f64 / hz * 1e6
} else {
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",
in_site as f64 / hz * 1e3,
attributed / hz * 1e3,
);
smarm::sleep(Duration::from_millis(150));
}
stop.store(true, Ordering::Relaxed);
producer.join().unwrap();
reserve.join().unwrap();
notify.join().unwrap();
background.join().unwrap();
println!("probe: DONE");
});
}