Files
Aura/crates/aura-bench/src/baseline.rs
T
claude 400105e2e2 feat(bench): CLI driver, fixed-cost surface, committed baselines + docs
Tasks 7-8 of the bench-harness plan plus the orchestrator's sizing pass —
this completes the phase-1 harness (closes #251).

Driver: run [--surface|--quick|--reps|--out] / pin over five surfaces,
report-only comparison (drift NOTICE at >=10%, never a failure), fingerprint
mismatch exits 1 with a baseline-vs-measured block, infra errors exit 2,
debug builds refuse to measure. The Bench project-facts line and the crate
README (discipline: quiet box, warmup+median, deliberate re-pins) land with
it.

Workload sizing, measured then corrected: the release engine outran the
plan's guesses by ~100x, leaving sub-second child walls where spawn jitter
reaches the 10% threshold and flaps the report. Full-size workloads now land
in whole seconds — engine 10M bars (~0.7s), ingest 24 months x 20 fresh
drains (~0.4s), campaigns on a 24-month archive with a ~22-month window and
a 5x5 member grid (sweep ~1.4s, heavy ~5.5s), fixed-cost as in-rep medians
over spawn batches (20x help, 10x run). Quick mode keeps the small
E2E-fixture shape.

The campaign fingerprint folds all three realization layers — sweep winner,
gate survivor ordinals, bootstrap trade counts (pooled or per-survivor sum)
— so a wrong-result regression in any stage fails the bench, not just a
wrong sweep winner. Baselines pinned on this host (commit-stamped), verified
by a follow-up run: five fingerprints OK at <=2% drift under load.

Verification: cargo test --workspace 1379 green (bench tests all quick-mode;
the release-binary E2Es are #[ignore]d behind -- --ignored per the
suite-wallclock discipline), clippy -D warnings clean, doc build clean.
2026-07-17 18:10:00 +02:00

165 lines
5.5 KiB
Rust

//! Baseline documents and the report-only comparison core. Pure functions —
//! everything here is unit-tested without running a single benchmark.
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
/// One committed baseline: `baselines/<surface>.json`.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BaselineDoc {
pub surface: String,
/// Flat metric map, e.g. `bars_per_s`, `wall_s`, `peak_rss_mb`.
pub metrics: BTreeMap<String, f64>,
/// Opaque determinism fingerprint; compared for byte equality.
pub fingerprint: String,
pub reps: u32,
pub host: HostInfo,
pub profile: String,
pub commit: String,
pub date: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct HostInfo {
pub hostname: String,
pub nproc: u32,
}
/// One measurement result, produced by a surface run.
#[derive(Clone, Debug, PartialEq)]
pub struct Measured {
pub surface: String,
pub metrics: BTreeMap<String, f64>,
pub fingerprint: String,
}
/// Timing drift at or beyond this fraction is reported as a NOTICE line.
pub const NOTICE_THRESHOLD: f64 = 0.10;
#[derive(Clone, Debug, PartialEq)]
pub struct Compared {
/// Per-metric relative delta, `(name, (measured - baseline) / baseline)`.
/// Metrics missing on either side are skipped.
pub deltas: Vec<(String, f64)>,
/// Metric names whose |delta| >= NOTICE_THRESHOLD.
pub notices: Vec<String>,
pub fingerprint_changed: bool,
pub host_mismatch: bool,
}
pub fn compare(baseline: &BaselineDoc, measured: &Measured, host: &HostInfo) -> Compared {
let mut deltas = Vec::new();
let mut notices = Vec::new();
for (name, base) in &baseline.metrics {
let Some(now) = measured.metrics.get(name) else { continue };
if *base == 0.0 {
continue; // no meaningful relative delta
}
let d = (now - base) / base;
if d.abs() >= NOTICE_THRESHOLD {
notices.push(name.clone());
}
deltas.push((name.clone(), d));
}
Compared {
deltas,
notices,
fingerprint_changed: baseline.fingerprint != measured.fingerprint,
host_mismatch: baseline.host != *host,
}
}
/// The process exit code for a full run: 1 if ANY surface's fingerprint
/// changed, else 0. (Infra errors exit 2 before this is ever consulted;
/// timing drift is report-only by design — the #251 cadence decision.)
pub fn exit_code(compared: &[Option<Compared>]) -> i32 {
if compared.iter().flatten().any(|c| c.fingerprint_changed) { 1 } else { 0 }
}
#[cfg(test)]
mod tests {
use super::*;
fn doc(metrics: &[(&str, f64)], fp: &str) -> BaselineDoc {
BaselineDoc {
surface: "s".into(),
metrics: metrics.iter().map(|(k, v)| (k.to_string(), *v)).collect(),
fingerprint: fp.into(),
reps: 3,
host: HostInfo { hostname: "h".into(), nproc: 24 },
profile: "release".into(),
commit: "abc".into(),
date: "2026-07-17".into(),
}
}
fn measured(metrics: &[(&str, f64)], fp: &str) -> Measured {
Measured {
surface: "s".into(),
metrics: metrics.iter().map(|(k, v)| (k.to_string(), *v)).collect(),
fingerprint: fp.into(),
}
}
fn host() -> HostInfo {
HostInfo { hostname: "h".into(), nproc: 24 }
}
#[test]
fn compare_reports_relative_drift_per_metric() {
let c = compare(&doc(&[("wall_s", 10.0)], "f"), &measured(&[("wall_s", 10.5)], "f"), &host());
assert_eq!(c.deltas.len(), 1);
assert_eq!(c.deltas[0].0, "wall_s");
assert!((c.deltas[0].1 - 0.05).abs() < 1e-12, "5% drift, got {}", c.deltas[0].1);
assert!(c.notices.is_empty(), "5% is below the 10% notice bar");
assert!(!c.fingerprint_changed);
assert!(!c.host_mismatch);
}
#[test]
fn compare_flags_notice_at_ten_percent() {
let c = compare(&doc(&[("wall_s", 10.0)], "f"), &measured(&[("wall_s", 11.0)], "f"), &host());
assert_eq!(c.notices, vec!["wall_s".to_string()]);
}
#[test]
fn compare_detects_fingerprint_change() {
let c = compare(&doc(&[], "old"), &measured(&[], "new"), &host());
assert!(c.fingerprint_changed);
}
#[test]
fn compare_flags_host_mismatch_informationally() {
let other = HostInfo { hostname: "elsewhere".into(), nproc: 8 };
let c = compare(&doc(&[], "f"), &measured(&[], "f"), &other);
assert!(c.host_mismatch);
assert!(!c.fingerprint_changed);
}
#[test]
fn compare_skips_metrics_missing_on_either_side() {
let c = compare(
&doc(&[("only_base", 1.0)], "f"),
&measured(&[("only_measured", 2.0)], "f"),
&host(),
);
assert!(c.deltas.is_empty());
}
#[test]
fn exit_code_is_one_only_on_fingerprint_change() {
let clean = compare(&doc(&[], "f"), &measured(&[], "f"), &host());
let changed = compare(&doc(&[], "f"), &measured(&[], "g"), &host());
assert_eq!(exit_code(&[Some(clean.clone()), None]), 0);
assert_eq!(exit_code(&[Some(clean), Some(changed)]), 1);
}
#[test]
fn baseline_doc_round_trips_through_json() {
let d = doc(&[("bars_per_s", 812345.0)], "last=0.1 n=5");
let json = serde_json::to_string_pretty(&d).expect("serialize");
let back: BaselineDoc = serde_json::from_str(&json).expect("parse");
assert_eq!(d, back);
}
}