//! shop-bench: perf harness driving external workloads (vtebench) and //! internal microbenches, comparing results against a stored per-machine //! baseline. See `docs/typed-protocol.md` Phase 0 for the design. //! //! use anyhow::Result; use clap::{Parser, Subcommand}; mod baseline; mod workloads; use baseline::Baseline; use workloads::{Registry, WorkloadResult}; #[derive(Parser)] #[command(name = "shop-bench", about = "shop perf harness")] struct Cli { #[command(subcommand)] cmd: Cmd, } #[derive(Subcommand)] enum Cmd { /// Run one or all workloads; compare against the machine baseline. Run { /// Workload name; omit to run all. workload: Option, /// Path to baseline JSON (defaults to /// `_private/docs/shop/bench-baselines/-latest.json`). #[arg(long)] baseline: Option, /// Skip the gate check; report numbers only. #[arg(long)] no_gate: bool, }, /// Run all workloads and write the results as a new baseline. Save { #[arg(long)] out: String, }, /// List registered workloads. List, } fn main() -> Result<()> { let cli = Cli::parse(); let reg = Registry::default_set(); match cli.cmd { Cmd::List => { for w in reg.iter() { println!("{}\t{}", w.name(), w.description()); } Ok(()) } Cmd::Run { workload, baseline, no_gate, } => { let results = run(®, workload.as_deref())?; let base = baseline.as_deref().map(Baseline::load).transpose()?; report(&results, base.as_ref(), !no_gate) } Cmd::Save { out } => { let results = run(®, None)?; let base = Baseline::from_results(&results); base.save(&out)?; eprintln!("wrote baseline to {out}"); Ok(()) } } } fn run(reg: &Registry, only: Option<&str>) -> Result> { let mut out = Vec::new(); for w in reg.iter() { if let Some(name) = only { if w.name() != name { continue; } } eprintln!("running {}...", w.name()); out.push(w.run()?); } if out.is_empty() { anyhow::bail!("no workload matched selector"); } Ok(out) } fn report(results: &[WorkloadResult], base: Option<&Baseline>, gate: bool) -> Result<()> { let mut breach = false; for r in results { println!("== {} ==", r.name); for (k, v) in &r.metrics { let cmp = base .and_then(|b| b.metric(&r.name, k)) .map(|prev| format!(" (baseline {prev:.4}, delta {:+.2}%)", pct(*v, *prev))); println!(" {k} = {v:.4}{}", cmp.unwrap_or_default()); } if gate { if let Some(b) = base { if let Err(msg) = b.check_gate(r) { eprintln!("GATE BREACH [{}]: {msg}", r.name); breach = true; } } } } if breach { anyhow::bail!("one or more gates breached"); } Ok(()) } fn pct(new: f64, old: f64) -> f64 { if old == 0.0 { 0.0 } else { (new - old) / old * 100.0 } }