Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

bench/examples/pushstage-probe.rs

//! One real push, decomposed into stages — the end-to-end gate on whether the
//! missing delta base cache is worth anything to gunnar in production.
//!
//! The per-object measurements say the `fs` backend's read path pays 1.4–3.5×
//! for having no delta base cache. That is only worth acting on if a **request**
//! moves, and on the serve side it mostly cannot: clone and fetch answer from
//! the bitmap and copy pack entries verbatim, neither of which resolves a chain.
//! The push side is different — `connectivity` walks the pushed commits and
//! their tree closure through `ObjectStore::read`, which is exactly the path
//! with no cache on it.
//!
//! ```text
//! pushstage-probe <source.git> <tip-oid> [runs]
//! ```
//!
//! Reuses [`gunnar_bench::stages::push`] rather than rebuilding a push driver,
//! so this and `gunnar.push_stages` are decomposing the same code.

use anyhow::{ensure, Context, Result};
use gix_hash::ObjectId;

fn main() -> Result<()> {
    let mut args = std::env::args_os().skip(1);
    let source = std::path::PathBuf::from(
        args.next()
            .context("usage: pushstage-probe <source.git> <tip> [runs]")?,
    );
    let tip = ObjectId::from_hex(
        args.next()
            .context("usage: pushstage-probe <source.git> <tip> [runs]")?
            .to_string_lossy()
            .trim()
            .as_bytes(),
    )?;
    let runs: usize = args
        .next()
        .map(|s| s.to_string_lossy().parse())
        .transpose()?
        .unwrap_or(5);

    let scratch = gunnar_bench::expand_home("~/scratch/deltachain/pushtargets");
    std::fs::create_dir_all(&scratch)?;

    let mut best: Option<gunnar_bench::stages::StageRun> = None;
    let mut walls: Vec<f64> = Vec::new();
    for i in 0..runs {
        let into = scratch.join(format!("t{i}.git"));
        // A push into a repository that already has the objects is a different
        // request: `connectivity` finds everything present without reading it.
        if into.exists() {
            std::fs::remove_dir_all(&into)?;
        }
        let run = gunnar_bench::stages::push(&source, &into, &tip, "refs/heads/master")?;
        walls.push(run.wall_ns as f64 / 1e6);
        if best.as_ref().is_none_or(|b| run.wall_ns < b.wall_ns) {
            best = Some(run);
        }
    }
    let run = best.context("no run completed")?;
    ensure!(
        run.objects > 0,
        "the push indexed zero objects, so every share below is a share of nothing"
    );

    walls.sort_by(f64::total_cmp);
    println!(
        "# {} objects, {} runs, wall min {:.1} ms / median {:.1} ms / max {:.1} ms",
        run.objects,
        runs,
        walls[0],
        walls[walls.len() / 2],
        walls[walls.len() - 1]
    );
    println!(
        "# attributed {:.1}% of process CPU; cpu owner = {}, memory owner = {}",
        100.0 * run.attributed_frac(),
        run.cpu_owner(),
        run.memory_owner()
    );
    println!(
        "{:<22} {:>10} {:>8} {:>8} {:>9} {:>11}",
        "stage", "wall_ms", "wall%", "cpu%", "reads", "read_MiB"
    );
    for r in &run.rows {
        println!(
            "{:<22} {:>10.2} {:>7.1}% {:>7.1}% {:>9} {:>11.2}",
            r.name,
            r.tally.excl_wall_ns as f64 / 1e6,
            100.0 * r.wall_frac,
            100.0 * r.cpu_frac,
            r.store_reads,
            r.store_read_bytes as f64 / 1_048_576.0
        );
    }
    Ok(())
}