use std::hint::black_box;
use std::time::Instant;
use znippy_plugin_git::index_layout::{
IndexEntry, ObjectIndex, PackedPayload, Rng, synthetic_entries,
};
use znippy_plugin_git::oid_index::OidLayout;
fn loadavg() -> (f64, String) {
let s = std::fs::read_to_string("/proc/loadavg").unwrap_or_default();
let one = s
.split_whitespace()
.next()
.and_then(|x| x.parse::<f64>().ok())
.unwrap_or(f64::NAN);
(one, s.trim().to_string())
}
#[derive(Clone, Copy)]
struct Stat {
med: f64,
lo: f64,
hi: f64,
}
impl Stat {
fn of(mut v: Vec<f64>) -> Self {
v.sort_by(|a, b| a.partial_cmp(b).unwrap());
Stat {
med: v[v.len() / 2],
lo: v[0],
hi: v[v.len() - 1],
}
}
fn spread(&self) -> f64 {
if self.med == 0.0 {
0.0
} else {
(self.hi - self.lo) / self.med
}
}
}
struct Queries {
owned: Vec<Vec<u8>>,
expected_hits: usize,
label: &'static str,
}
impl Queries {
fn refs(&self) -> Vec<&[u8]> {
self.owned.iter().map(|o| o.as_slice()).collect()
}
}
fn queries(
present: &[IndexEntry],
absent: &[IndexEntry],
total: usize,
hit_pct: usize,
label: &'static str,
seed: u64,
) -> Queries {
let mut rng = Rng(seed);
let mut owned = Vec::with_capacity(total);
let mut expected_hits = 0usize;
for _ in 0..total {
if (rng.next_u64() % 100) < hit_pct as u64 {
owned.push(
present[(rng.next_u64() as usize) % present.len()]
.oid
.clone(),
);
expected_hits += 1;
} else {
owned.push(absent[(rng.next_u64() as usize) % absent.len()].oid.clone());
}
}
Queries {
owned,
expected_hits,
label,
}
}
#[inline]
fn fold_ordinals(rows: &[Option<u32>]) -> u64 {
let mut acc = 0u64;
for r in rows {
if let Some(o) = r {
acc = acc.wrapping_add(*o as u64);
}
}
black_box(acc)
}
fn time_ordinals(
idx: &dyn ObjectIndex,
q: &Queries,
batch: usize,
runs: usize,
dry: bool,
) -> (Stat, u64) {
let refs = q.refs();
let mut samples = Vec::with_capacity(runs);
let mut checksum = 0u64;
for _ in 0..runs {
let t = Instant::now();
let mut acc = 0u64;
let mut hits = 0usize;
for chunk in refs.chunks(batch) {
if dry {
acc = acc.wrapping_add(black_box(chunk).len() as u64);
continue;
}
let rows = idx.ordinals_batch(chunk);
hits += rows.iter().filter(|r| r.is_some()).count();
acc = acc.wrapping_add(fold_ordinals(&rows));
}
let ns = t.elapsed().as_nanos() as f64 / refs.len() as f64;
if !dry {
assert_eq!(hits, q.expected_hits, "hit count wrong at batch {batch}");
}
samples.push(ns);
checksum = acc;
}
(Stat::of(samples), black_box(checksum))
}
fn build(entries: &[IndexEntry], layout: OidLayout, runs: usize) -> (Stat, PackedPayload) {
let mut samples = Vec::with_capacity(runs);
let mut last: Option<PackedPayload> = None;
for _ in 0..runs {
drop(last.take());
let t = Instant::now();
let idx = PackedPayload::build_with_oid_layout(entries, layout).expect("build");
samples.push(t.elapsed().as_secs_f64() * 1e3);
last = Some(idx);
}
(Stat::of(samples), last.unwrap())
}
fn main() {
let thp = znippy_zoomies::stree::thp_enable_for_process();
eprintln!("thp_enabled={}", thp);
let mut sizes: Vec<usize> = vec![1_000, 100_000, 1_000_000, 4_000_000];
let mut total_queries = 100_000usize;
let mut runs = 5usize;
let mut oid_len = 20usize;
let mut rot = 0usize;
let mut only: Option<usize> = None;
let mut dry = false;
let mut buildruns: Option<usize> = None;
let mut force = false;
for a in std::env::args().skip(1) {
let (k, v) = a.split_once('=').unwrap_or((a.as_str(), ""));
match k {
"sizes" => sizes = v.split(',').map(|x| x.parse().unwrap()).collect(),
"queries" => total_queries = v.parse().unwrap(),
"runs" => runs = v.parse().unwrap(),
"oid" => oid_len = v.parse().unwrap(),
"rot" => rot = v.parse::<usize>().unwrap() % 3,
"only" => only = Some(v.parse::<usize>().unwrap() % 3),
"dry" => dry = true,
"buildruns" => buildruns = Some(v.parse().unwrap()),
"force" => force = true,
other => panic!("unknown argument `{other}`"),
}
}
let active: Vec<usize> = match only {
Some(a) => vec![a],
None => (0..3).map(|slot| (slot + rot) % 3).collect(),
};
let arms = OidLayout::ALL;
let (load1, load) = loadavg();
println!("# stree keyspace alignment — oid→ordinal only");
println!();
println!("host loadavg at start: `{load}` (1-min {load1:.2})");
println!(
"oid width {oid_len} B · {total_queries} lookups per timed cell · {runs} runs per cell · \
rotation {rot} (first arm: {})",
arms[rot % 3].name()
);
if load1 > 4.0 && !force {
println!();
println!(
"**REFUSING TO MEASURE**: 1-minute load is {load1:.2}. oden is shared and a figure \
taken on a busy box is worse than no figure."
);
std::process::exit(2);
}
let mut all_spreads: Vec<f64> = Vec::new();
for &n in &sizes {
let present = synthetic_entries(n, oid_len, 0x5EED_0001 ^ n as u64);
let absent = synthetic_entries(n.clamp(1000, 200_000), oid_len, 0xDEAD_0002 ^ n as u64);
let mixes = [
queries(
&present,
&absent,
total_queries,
100,
"100% hit (want resolution)",
1,
),
queries(
&present,
&absent,
total_queries,
10,
"10% hit (have negotiation)",
3,
),
];
let mut built: Vec<Option<(Stat, PackedPayload)>> = (0..3).map(|_| None).collect();
for &a in &active {
built[a] = Some(build(&present, arms[a], buildruns.unwrap_or(runs)));
}
println!();
println!("## {n} objects");
println!();
let mut phases = [usize::MAX; 3];
for &a in &active {
let phase = built[a].as_ref().unwrap().1.keyspace_phase();
phases[a] = phase;
match arms[a] {
OidLayout::Aligned64 => {
assert_eq!(phase, 0, "aligned64 is at phase {phase}, not 0")
}
OidLayout::Compact64Alloc => {
assert_eq!(phase, 24, "compact24+align is at phase {phase}, not 24")
}
OidLayout::Compact => {}
}
}
if only.is_none() && !dry {
assert_ne!(
phases[0], phases[2],
"compact24 and aligned64 share a phase"
);
for m in &mixes {
let refs = m.refs();
let base = built[0].as_ref().unwrap().1.ordinals_batch(&refs);
assert_eq!(
base.iter().filter(|r| r.is_some()).count(),
m.expected_hits,
"{} hit rate is not what was generated",
m.label
);
for a in 1..3 {
assert_eq!(
built[a].as_ref().unwrap().1.ordinals_batch(&refs),
base,
"{} disagrees with compact24 on the {} workload",
arms[a].name(),
m.label
);
}
}
}
let (_, load_here) = loadavg();
println!("loadavg: `{load_here}`");
println!();
println!(
"keyspace phase (bytes into the cache line): {}",
active
.iter()
.map(|&a| format!("{}={}", arms[a].name(), phases[a]))
.collect::<Vec<_>>()
.join(" ")
);
println!();
for &a in &active {
let b = built[a].as_ref().unwrap();
all_spreads.push(b.0.spread());
println!(
"DATA\t{n}\tbuild\tbuild_ms\t{}\t{:.6}\t{:.6}\t{:.6}",
arms[a].name(),
b.0.med,
b.0.lo,
b.0.hi
);
}
for m in &mixes {
println!();
println!("### {n} objects · {}", m.label);
println!();
for &batch in &[1usize, 100, 1000] {
let mut cells: Vec<Option<(Stat, u64)>> = (0..3).map(|_| None).collect();
for &a in &active {
cells[a] = Some(time_ordinals(
&built[a].as_ref().unwrap().1,
m,
batch,
runs,
dry,
));
}
if only.is_none() && !dry {
assert_eq!(
cells[0].unwrap().1,
cells[1].unwrap().1,
"batch {batch}: checksums differ"
);
assert_eq!(
cells[0].unwrap().1,
cells[2].unwrap().1,
"batch {batch}: checksums differ"
);
}
for &a in &active {
let c = cells[a].unwrap();
all_spreads.push(c.0.spread());
println!(
"DATA\t{n}\t{}\tbatch{batch}\t{}\t{:.6}\t{:.6}\t{:.6}",
m.label.split(' ').next().unwrap(),
arms[a].name(),
c.0.med,
c.0.lo,
c.0.hi
);
}
}
}
}
all_spreads.sort_by(|x, y| x.partial_cmp(y).unwrap());
let med = all_spreads[all_spreads.len() / 2];
let p90 = all_spreads[all_spreads.len() * 9 / 10];
let (_, load_end) = loadavg();
println!();
println!("## Noise band");
println!();
println!(
"Across all {} timed cells, run-to-run spread `(max-min)/median` was median **{:.1}%**, \
p90 **{:.1}%**, worst **{:.1}%**.",
all_spreads.len(),
med * 100.0,
p90 * 100.0,
all_spreads.last().unwrap() * 100.0
);
println!();
println!("loadavg at end: `{load_end}`");
}