//! 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::(); let (tx_bc, rx_bc) = smarm::channel::(); 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"); }); }