use std::alloc::{GlobalAlloc, Layout, System};
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use znippy_plugin_git::exploded::NoSink;
use znippy_plugin_git::object::GitHashKind;
use znippy_plugin_git::pack_walk::walk;
use znippy_plugin_git::resolve::{NoBases, resolve_walked};
static LIVE: AtomicUsize = AtomicUsize::new(0);
static PEAK: AtomicUsize = AtomicUsize::new(0);
struct Counting;
impl Counting {
fn grew(by: usize) {
let now = LIVE.fetch_add(by, Ordering::Relaxed).saturating_add(by);
PEAK.fetch_max(now, Ordering::Relaxed);
}
fn shrank(by: usize) {
let _ = LIVE.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
Some(v.saturating_sub(by))
});
}
}
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
let p = unsafe { System.alloc(l) };
if !p.is_null() {
Self::grew(l.size());
}
p
}
unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
Self::shrank(l.size());
unsafe { System.dealloc(p, l) }
}
unsafe fn realloc(&self, p: *mut u8, l: Layout, new: usize) -> *mut u8 {
let q = unsafe { System.realloc(p, l, new) };
if !q.is_null() {
if new >= l.size() {
Self::grew(new - l.size());
} else {
Self::shrank(l.size() - new);
}
}
q
}
unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 {
let p = unsafe { System.alloc_zeroed(l) };
if !p.is_null() {
Self::grew(l.size());
}
p
}
}
#[global_allocator]
static A: Counting = Counting;
fn real_packs() -> Vec<PathBuf> {
let mut out: Vec<(u64, PathBuf)> = Vec::new();
for root in [
"/home/rickard/git",
"/home/rickard/scratch/gunnar-bench-fixtures",
] {
let Ok(repos) = std::fs::read_dir(root) else {
continue;
};
for repo in repos.flatten() {
for sub in [".git/objects/pack", "objects/pack"] {
let Ok(files) = std::fs::read_dir(repo.path().join(sub)) else {
continue;
};
for f in files.flatten() {
let p = f.path();
if p.extension().is_some_and(|e| e == "pack") {
let len = f.metadata().map(|m| m.len()).unwrap_or(0);
if len < (32 << 20) {
out.push((len, p));
}
}
}
}
}
}
out.sort_by_key(|(len, _)| std::cmp::Reverse(*len));
out.into_iter().map(|(_, p)| p).collect()
}
#[test]
fn resolving_a_pack_does_not_hold_the_whole_object_set() {
let packs = real_packs();
if packs.is_empty() {
eprintln!("no real pack on this machine; nothing to measure");
return;
}
let mut measured = 0usize;
let mut worst: Option<(f64, String)> = None;
let mut total_secs = 0.0f64;
let mut total_payload = 0u64;
for pack_path in packs.iter().take(12) {
let Ok(pack) = std::fs::read(pack_path) else {
continue;
};
let Ok(w) = walk(&pack, 20) else { continue };
LIVE.store(0, Ordering::Relaxed);
PEAK.store(0, Ordering::Relaxed);
let t0 = std::time::Instant::now();
let rows = match resolve_walked(&pack, &w, GitHashKind::Sha1, 0, &NoBases, &NoSink) {
Ok(rows) => rows,
Err(e) => {
eprintln!("SKIP {}\n {e}", pack_path.display());
continue;
}
};
let elapsed = t0.elapsed();
let peak = PEAK.load(Ordering::Relaxed);
total_secs += elapsed.as_secs_f64();
let payload: u64 = rows.iter().map(|r| r.uncompressed_size).sum();
if payload < 4_000_000 {
continue;
}
let ratio = peak as f64 / payload as f64;
eprintln!(
"{:.2}x peak {:>7.1} MB / payload {:>7.1} MB {:>6} objects {:>7.3}s {}",
ratio,
peak as f64 / 1e6,
payload as f64 / 1e6,
rows.len(),
elapsed.as_secs_f64(),
pack_path.display(),
);
measured += 1;
total_payload += payload;
if worst.as_ref().is_none_or(|(w, _)| ratio > *w) {
worst = Some((ratio, pack_path.display().to_string()));
}
}
eprintln!(
"TOTAL {measured} packs, {:.1} MB inflated payload, {total_secs:.3}s in resolve_walked \
({:.1} MB/s)",
total_payload as f64 / 1e6,
total_payload as f64 / 1e6 / total_secs,
);
assert!(
measured >= 3,
"only {measured} pack(s) were big enough to measure — too few to prove anything about \
scaling"
);
let (ratio, which) = worst.expect("measured >= 3 means there is a worst");
assert!(
ratio < 0.75,
"resolving {which} peaked at {ratio:.2}x its own inflated payload. The resolver is \
holding the object set, not a delta-base working set."
);
}