crates/mimir-room/tests/advance_bench.rs
//! **The advance beats the refold — measured.** Micro-sized for a weak
//! machine: `HISTORY` commits rewrite the same `FNS` functions, so the log
//! holds `HISTORY × FNS` node events while the room keeps `FNS` nodes. The
//! full build folds the whole history; the advance reads one commit's
//! appended files and reassembles. The ratio grows with history — on the
//! production log (every commit ever over ~45 repos) it is the whole fold.
#[allow(dead_code)]
#[path = "room/common.rs"]
mod common;
use std::time::Instant;
use common::*;
use mimir_ingest::Consumer;
use mimir_room::{Room, Scope};
use mimir_schema::{EdgeKind, Origin};
const FNS: usize = 250;
const HISTORY: usize = 40;
const BENCH: &str = "bench";
/// Commit `c`'s dump: the same functions, every body moved (`ty` carries the
/// churn so each commit writes a fresh `+` event per function).
fn step(c: usize) -> Step {
let mut nodes = Vec::with_capacity(FNS);
let mut edges = Vec::with_capacity(FNS);
for i in 0..FNS {
let id = format!("b fn{i}");
let mut n = node(&id, "fn", "b", Origin::Local, true);
n.ty = Some(format!("c{c}"));
nodes.push(n);
edges.push(edge(&id, &format!("b fn{}", (i + 1) % FNS), EdgeKind::Calls));
}
Step { owner: "b@0.1.0:lib", nodes, edges, binary: None, ring: vec![] }
}
async fn run_one(log: mimir_ingest::EventLog, tmp: &std::path::Path, s: Step, tag: usize) -> mimir_ingest::EventLog {
let src = tmp.join(format!("src-{BENCH}"));
std::fs::write(src.join("src/lib.rs"), format!("// one more: {tag}\n")).unwrap();
nornir_git::gitio::commit_all(&src, "one more").unwrap();
let mut c = Consumer::boot(log, Scripted(vec![s])).await.unwrap();
let rep = c.run(&repo_spec(tmp, BENCH)).await.unwrap();
assert!(rep.iter().all(|r| r.status == "ok"), "{rep:?}");
c.into_parts().0
}
#[tokio::test(flavor = "current_thread")]
async fn an_advance_beats_the_refold() {
let _g = serial().await;
Room::clear_cache();
Room::set_cache_capacity(16);
let tmp = tempfile::tempdir().unwrap();
let log = open_log(tmp.path()).await;
let log = consume(log, tmp.path(), BENCH, (0..HISTORY).map(step).collect()).await;
// the refold: a cold open over the whole history
let t = Instant::now();
let full = Room::open(&log, &Scope::current()).await.unwrap();
let full_ms = t.elapsed().as_secs_f64() * 1000.0;
assert!(!full.cached && !full.advanced, "cold");
let full_stats = serde_json::to_value(full.room.stats()).unwrap();
// one more commit lands: the kept fold advances by its delta
let log = run_one(log, tmp.path(), step(HISTORY), HISTORY).await;
let t = Instant::now();
let adv = Room::open(&log, &Scope::current()).await.unwrap();
let adv_ms = t.elapsed().as_secs_f64() * 1000.0;
assert!(!adv.cached && adv.advanced, "the delta path served it");
// RED before: the old head's snapshot stayed beside the advanced one
assert_eq!(Room::cached_count(), 1, "the old head's room left with its fold: one room in memory, not two");
// the room is whole (same shape as the refold saw, one seq later)
let adv_stats = serde_json::to_value(adv.room.stats()).unwrap();
assert_eq!(adv_stats["nodes"], full_stats["nodes"]);
assert_eq!(adv_stats["edges"], full_stats["edges"]);
eprintln!(
"advance bench: history {HISTORY} commits x {FNS} fns — refold {full_ms:.0} ms, advance {adv_ms:.0} ms ({:.1}x)",
full_ms / adv_ms
);
assert!(adv_ms < full_ms, "the advance ({adv_ms:.0} ms) must beat the refold ({full_ms:.0} ms)");
// ---- the universe arm, over the same log: the ring room's kept fold
// advances too (the dependency universe does not move between commits)
// a ring view keeps no fold by default (MIMIR_RING_KEEP_FOLD): this arm opts in
Room::set_ring_keeps_fold(true);
let ring_scope = Scope::current().with_universe(true);
let t = Instant::now();
let full = Room::open(&log, &ring_scope).await.unwrap();
let ring_full_ms = t.elapsed().as_secs_f64() * 1000.0;
assert!(!full.cached && !full.advanced, "cold ring build");
let full_stats = serde_json::to_value(full.room.stats()).unwrap();
let log = run_one(log, tmp.path(), step(HISTORY + 1), HISTORY + 1).await;
let t = Instant::now();
let adv = Room::open(&log, &ring_scope).await.unwrap();
let ring_adv_ms = t.elapsed().as_secs_f64() * 1000.0;
assert!(!adv.cached && adv.advanced, "the ring room's delta path served it");
let adv_stats = serde_json::to_value(adv.room.stats()).unwrap();
assert_eq!(adv_stats["nodes"], full_stats["nodes"]);
assert_eq!(adv_stats["edges"], full_stats["edges"]);
assert_eq!(adv_stats["universe_rows"], full_stats["universe_rows"]);
eprintln!(
"advance bench (ring): history {} commits x {FNS} fns — refold {ring_full_ms:.0} ms, advance {ring_adv_ms:.0} ms ({:.1}x)",
HISTORY + 1,
ring_full_ms / ring_adv_ms
);
assert!(ring_adv_ms < ring_full_ms, "the ring advance ({ring_adv_ms:.0} ms) must beat the ring refold ({ring_full_ms:.0} ms)");
}