bench/examples/advertise-probe.rs
//! **`ls-refs` cost against ref count, on a real store.**
//!
//! Point it at a directory holding the prepared znippy stores
//! `repo<N>.git.gunnar-znippy` for a ladder of ref counts (the fixture builder
//! makes 40 / 400 / 4000 refs). It opens each store once and
//! times one `command=ls-refs` with `peel`, printing wall µs/ref and the
//! op-count that is the real red: `reads`, the full exploded-table pulls one
//! advertisement makes, which is `advertise_from` re-peeling every annotated tag
//! every time. The timing driver is `gunnar_bench::advertise::probe`, kept in
//! the library so a persisted `gunnar.advertise_*` arm reuses it, not a twin.
//!
//! ```text
//! GUNNAR_ENGINE=znippy cargo run --release --example advertise-probe \
//! --manifest-path bench/Cargo.toml -- <fixtures-dir> [sha1|sha256]
//! ```
//!
//! A diagnostic, its own target for the reason `readside-probe` is: a table a
//! human reads once, not a `BenchRun` row. The 1-minute load is printed beside
//! the numbers so a contaminated run is visible: load < 3 or it is not a
//! measurement.
use std::path::PathBuf;
use anyhow::{bail, Context, Result};
fn one_min_load() -> f64 {
std::fs::read_to_string("/proc/loadavg")
.ok()
.and_then(|s| s.split_whitespace().next().map(|t| t.to_owned()))
.and_then(|t| t.parse().ok())
.unwrap_or(f64::NAN)
}
fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
let dir = PathBuf::from(
args.next()
.context("usage: advertise-probe <fixtures-dir> [sha1|sha256] [N,N,...]")?,
);
let kind = match args.next().as_deref() {
None | Some("sha256") => gix_hash::Kind::Sha256,
Some("sha1") => gix_hash::Kind::Sha1,
Some(other) => bail!("unknown hash kind {other:?}"),
};
let counts: Vec<u32> = match args.next() {
Some(list) => list
.split(',')
.map(|s| s.trim().parse().context("ref count"))
.collect::<Result<_>>()?,
None => vec![40, 400, 4000],
};
let load_before = one_min_load();
println!(
"# advertise-probe engine={:?} 1-min load at start = {load_before:.2}",
std::env::var("GUNNAR_ENGINE").unwrap_or_else(|_| "<default>".into())
);
if load_before >= 3.0 {
eprintln!(
"WARNING: 1-min load {load_before:.2} >= 3 — this is NOT a measurement. \
Numbers below are for shape only; do not record them."
);
}
println!(
"{:>5} {:>7} {:>7} {:>8} {:>12} {:>10} {:>8} {:>8}",
"mode", "refs", "peeled", "reads", "wall_us", "us/ref", "reads/r", "headers"
);
let mut row = |mode: &str, a: &gunnar_bench::advertise::Advertisement| {
println!(
"{:>5} {:>7} {:>7} {:>8} {:>12.1} {:>10.3} {:>8.2} {:>8}",
mode,
a.refs_held,
a.peeled_lines,
a.reads,
a.best_wall_ns as f64 / 1000.0,
a.us_per_ref(),
a.reads_per_ref(),
a.headers,
);
};
for n in counts {
let store = dir.join(format!("repo{n}.git.gunnar-znippy"));
if !store.is_dir() {
eprintln!("skip: {} is not a prepared store", store.display());
continue;
}
let warm = gunnar_bench::advertise::probe(&store, kind, 3, 5)
.with_context(|| format!("probing (warm) {}", store.display()))?;
row("warm", &warm);
// `cold`: page cache dropped before each run — the appliance's
// cache-pressured regime, where each exploded read is a real batch decode.
let cold = gunnar_bench::advertise::probe_cold(&store, kind, 4)
.with_context(|| format!("probing (cold) {}", store.display()))?;
row("cold", &cold);
}
let load_after = one_min_load();
println!("# 1-min load at end = {load_after:.2}");
if load_before >= 3.0 || load_after >= 3.0 {
println!("# CONTAMINATED (load >= 3 at some point) — shape only, not for bench_history.");
}
Ok(())
}