Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ uuid = { version = "1", features = ["v4"] }
[workspace.lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
redundant_closure_for_method_calls = "warn"
cloned_instead_of_copied = "warn"
implicit_clone = "warn"
map_unwrap_or = "warn"
uninlined_format_args = "warn"
manual_let_else = "warn"
semicolon_if_nothing_returned = "warn"
38 changes: 13 additions & 25 deletions crates/gmeta-bench/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,17 @@ pub fn run() -> Result<()> {
return Ok(());
}

println!("{}benchmarking {} key reads...{}", BOLD, total, RESET);
println!("{BOLD}benchmarking {total} key reads...{RESET}");

let mut durations: Vec<Duration> = Vec::with_capacity(total);
let mut sizes: Vec<usize> = Vec::with_capacity(total);
let mut errors = 0u64;

for (target_type_str, target_value, key) in &keys {
let t0 = Instant::now();
let target_type = match target_type_str.parse::<TargetType>() {
Ok(tt) => tt,
Err(_) => {
errors += 1;
continue;
}
let Ok(target_type) = target_type_str.parse::<TargetType>() else {
errors += 1;
continue;
};
let target = if target_type == TargetType::Project {
Target::project()
Expand All @@ -64,7 +61,7 @@ pub fn run() -> Result<()> {

let n = durations.len();
if n == 0 {
println!("no values could be read ({} errors)", errors);
println!("no values could be read ({errors} errors)");
return Ok(());
}

Expand Down Expand Up @@ -93,18 +90,12 @@ pub fn run() -> Result<()> {
if errors == 1 { " error" } else { " errors" },
RESET,
);
println!(
" {}mean{} {}{:>10.6} s{}",
DIM, RESET, YELLOW, mean_s, RESET
);
println!(" {}p50{} {}{:>10.6} s{}", DIM, RESET, GREEN, p50, RESET);
println!(" {}p95{} {}{:>10.6} s{}", DIM, RESET, YELLOW, p95, RESET);
println!(" {}p99{} {}{:>10.6} s{}", DIM, RESET, RED, p99, RESET);
println!(" {}max{} {}{:>10.6} s{}", DIM, RESET, RED, max_s, RESET);
println!(
" {}total{} {}{:>10.6} s{}",
DIM, RESET, CYAN, total_s, RESET
);
println!(" {DIM}mean{RESET} {YELLOW}{mean_s:>10.6} s{RESET}");
println!(" {DIM}p50{RESET} {GREEN}{p50:>10.6} s{RESET}");
println!(" {DIM}p95{RESET} {YELLOW}{p95:>10.6} s{RESET}");
println!(" {DIM}p99{RESET} {RED}{p99:>10.6} s{RESET}");
println!(" {DIM}max{RESET} {RED}{max_s:>10.6} s{RESET}");
println!(" {DIM}total{RESET} {CYAN}{total_s:>10.6} s{RESET}");

// Size histogram
let boundaries: &[(usize, &str)] = &[
Expand All @@ -126,16 +117,13 @@ pub fn run() -> Result<()> {
}

println!();
println!("{}value sizes:{}", BOLD, RESET);
println!("{BOLD}value sizes:{RESET}");
let max_count = counts.iter().copied().max().unwrap_or(1).max(1);
let bar_width = 30usize;
for ((_, label), count) in boundaries.iter().zip(counts.iter()) {
let filled = ((*count as f64 / max_count as f64) * bar_width as f64).round() as usize;
let bar = format!("{}{}{}", BLUE, "#".repeat(filled), RESET);
println!(
" {}{}{} {:<30} {}{}{}",
DIM, label, RESET, bar, CYAN, count, RESET,
);
println!(" {DIM}{label}{RESET} {bar:<30} {CYAN}{count}{RESET}",);
}

Ok(())
Expand Down
73 changes: 25 additions & 48 deletions crates/gmeta-bench/src/fanout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,7 @@ fn fake_sha(n: u64) -> String {
format!("{:016x}{:016x}{:08x}", x, y, (x ^ y) as u32)
}
fn print_header(label: &str) {
println!(
"\n{}{} ══════════════════════════════════════════════{}\n",
BOLD, label, RESET
);
println!("\n{BOLD}{label} ══════════════════════════════════════════════{RESET}\n");
}

fn fmt_ms(secs: f64) -> String {
Expand Down Expand Up @@ -228,7 +225,7 @@ fn pack_size_bytes(repo_path: &std::path::Path) -> Result<u64> {
for entry in std::fs::read_dir(&pack_dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().map(|e| e == "pack").unwrap_or(false) {
if path.extension().is_some_and(|e| e == "pack") {
total += entry.metadata()?.len();
}
}
Expand Down Expand Up @@ -285,7 +282,7 @@ struct SchemeResult {
const SAMPLE: usize = 1_000;

fn entry_content(sha: &str) -> Vec<u8> {
format!("{{\"sha\":\"{}\"}}", sha).into_bytes()
format!("{{\"sha\":\"{sha}\"}}").into_bytes()
}

#[allow(clippy::unwrap_used, clippy::expect_used)]
Expand Down Expand Up @@ -499,12 +496,10 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
let max_diff = maxf!(diff_secs);

println!(
"{}Write A — batch: {} new entries → 1 incremental tree (into {}-object base){}",
BOLD, SAMPLE, n_base, RESET
"{BOLD}Write A — batch: {SAMPLE} new entries → 1 incremental tree (into {n_base}-object base){RESET}"
);
println!(
" {}(only the touched shard subtrees are rebuilt; unchanged subtrees reused by OID){}",
DIM, RESET
" {DIM}(only the touched shard subtrees are rebuilt; unchanged subtrees reused by OID){RESET}"
);
for r in results {
let b = bar(r.write_batch_secs, max_write_batch, bar_w, YELLOW);
Expand All @@ -519,12 +514,10 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
}

println!(
"\n{}Write B — sequential: 1 entry → incremental tree → commit × {} (chained parents){}",
BOLD, SAMPLE, RESET
"\n{BOLD}Write B — sequential: 1 entry → incremental tree → commit × {SAMPLE} (chained parents){RESET}"
);
println!(
" {}(models frequent single-change serializes; total time for {} commits){}",
DIM, SAMPLE, RESET
" {DIM}(models frequent single-change serializes; total time for {SAMPLE} commits){RESET}"
);
for r in results {
let b = bar(r.write_seq_secs, max_write_seq, bar_w, YELLOW);
Expand All @@ -542,11 +535,8 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!("\n{}Read 1 000 objects by known SHA{}", BOLD, RESET);
println!(
" {}(lower = faster; tree.get_path() walk per lookup){}",
DIM, RESET
);
println!("\n{BOLD}Read 1 000 objects by known SHA{RESET}");
println!(" {DIM}(lower = faster; tree.get_path() walk per lookup){RESET}");
for r in results {
let b = bar(r.read_1k_secs, max_read, bar_w, GREEN);
println!(
Expand All @@ -559,13 +549,13 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!("\n{}Diff base tree vs base + 1 000 changes{}", BOLD, RESET);
println!("\n{BOLD}Diff base tree vs base + 1 000 changes{RESET}");
println!(
" {}(lower = faster; git diff-tree subprocess){} [changed blobs: {}{}{}]",
DIM,
RESET,
CYAN,
results.first().map(|r| r.diff_count).unwrap_or(0),
results.first().map_or(0, |r| r.diff_count),
RESET
);
for r in results {
Expand All @@ -580,11 +570,8 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!(
"\n{}Pack file size (after git gc --aggressive){}",
BOLD, RESET
);
println!(" {}(lower = better compression){}", DIM, RESET);
println!("\n{BOLD}Pack file size (after git gc --aggressive){RESET}");
println!(" {DIM}(lower = better compression){RESET}");
for r in results {
let mb = r.pack_bytes as f64 / 1_048_576.0;
let b = bar(r.pack_bytes as f64, max_pack, bar_w, MAGENTA);
Expand All @@ -598,11 +585,8 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!("\n{}Loose object count (before gc){}", BOLD, RESET);
println!(
" {}(tree objects + blob objects written to ODB){}",
DIM, RESET
);
println!("\n{BOLD}Loose object count (before gc){RESET}");
println!(" {DIM}(tree objects + blob objects written to ODB){RESET}");
for r in results {
let b = bar(
r.loose_objects_before_gc as f64,
Expand All @@ -620,10 +604,7 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!(
"\n{}Per-object read latency (avg over 1 000){}",
BOLD, RESET
);
println!("\n{BOLD}Per-object read latency (avg over 1 000){RESET}");
for r in results {
let per_obj = r.read_1k_secs / SAMPLE as f64;
println!(
Expand All @@ -635,7 +616,7 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}

println!("\n{} ── Verdict ──{} (bucket counts: ", BOLD, RESET);
println!("\n{BOLD} ── Verdict ──{RESET} (bucket counts: ");
for r in results {
print!(
" {}{}{} → {}{}{} buckets",
Expand All @@ -648,7 +629,7 @@ fn print_report(results: &[SchemeResult], n_base: usize) {
);
}
println!(")\n");
println!("{} ── Verdict ──{}", BOLD, RESET);
println!("{BOLD} ── Verdict ──{RESET}");
let fastest_write = results
.iter()
.min_by(|a, b| {
Expand Down Expand Up @@ -707,17 +688,13 @@ pub fn run(n_objects: usize) -> Result<()> {
let schemes = [Scheme::First2, Scheme::First3, Scheme::First2Next2];

println!(
"\n{}gmeta fanout benchmark{} — {}{} base objects{}, {} new per test",
BOLD, RESET, CYAN, n_objects, RESET, SAMPLE
"\n{BOLD}gmeta fanout benchmark{RESET} — {CYAN}{n_objects} base objects{RESET}, {SAMPLE} new per test"
);
println!("{}running all three schemes in parallel{}\n", DIM, RESET);
println!("{DIM}running all three schemes in parallel{RESET}\n");

// Pre-generate all SHAs we'll ever need (base + SAMPLE extra for write test)
let total_needed = n_objects + SAMPLE;
print!(
"{}generating {} synthetic SHAs...{} ",
DIM, total_needed, RESET
);
print!("{DIM}generating {total_needed} synthetic SHAs...{RESET} ");
let _ = std::io::stdout().flush();
let t0 = Instant::now();
let shas: Arc<Vec<String>> = Arc::new((0..total_needed as u64).map(fake_sha).collect());
Expand Down Expand Up @@ -774,7 +751,7 @@ pub fn run(n_objects: usize) -> Result<()> {
for handle in handles {
match handle.join() {
Ok(Ok(r)) => results.push(r),
Ok(Err(e)) => errors.push(format!("{:?}", e)),
Ok(Err(e)) => errors.push(format!("{e:?}")),
Err(_) => errors.push("thread panicked".to_string()),
}
}
Expand All @@ -783,7 +760,7 @@ pub fn run(n_objects: usize) -> Result<()> {

if !errors.is_empty() {
for e in &errors {
eprintln!("{}error:{} {}", RED, RESET, e);
eprintln!("{RED}error:{RESET} {e}");
}
anyhow::bail!("one or more scheme benchmarks failed");
}
Expand All @@ -800,7 +777,7 @@ pub fn run(n_objects: usize) -> Result<()> {
// the terminal doesn't support ANSI cursor movement.
let n_lines = schemes.len() as u16 + 1; // +1 for the separator row
// Move cursor up n_lines
print!("\x1b[{}A", n_lines);
print!("\x1b[{n_lines}A");

println!(
" {}{:<20} {:<12} {:<14} {:<14} {:<12} {:<10}{}",
Expand Down Expand Up @@ -842,7 +819,7 @@ pub fn run(n_objects: usize) -> Result<()> {
for r in &results {
println!("\n{}── {} ──{}", DIM, r.scheme.name(), RESET);
for line in &r.log {
println!("{}", line);
println!("{line}");
}
}

Expand Down
29 changes: 13 additions & 16 deletions crates/gmeta-bench/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ fn generate_history(
let mut meta_counter: u64 = 0;
let next_meta = |c: &mut u64| -> Vec<u8> {
*c += 1;
format!("meta-value-{}", c).into_bytes()
format!("meta-value-{c}").into_bytes()
};

let mut commit_chain: Vec<gix::ObjectId> = Vec::with_capacity(n_commits);
Expand Down Expand Up @@ -434,12 +434,10 @@ fn fmt_us(secs: f64) -> String {
}
pub fn run(n_commits: usize) -> Result<()> {
println!(
"\n{}gmeta history-walker benchmark{} — {}{} commits to generate{}",
BOLD, RESET, CYAN, n_commits, RESET,
"\n{BOLD}gmeta history-walker benchmark{RESET} — {CYAN}{n_commits} commits to generate{RESET}",
);
println!(
"{}rules: 95% introduce / 5% modify, 1–200 values/commit (low-weighted), prune at >{} values (keep {}){}\n",
DIM, PRUNE_THRESHOLD, PRUNE_KEEP, RESET,
"{DIM}rules: 95% introduce / 5% modify, 1–200 values/commit (low-weighted), prune at >{PRUNE_THRESHOLD} values (keep {PRUNE_KEEP}){RESET}\n",
);

// Temp bare repo — gix uses on-disk ODB (no mempack equivalent).
Expand All @@ -454,7 +452,7 @@ pub fn run(n_commits: usize) -> Result<()> {
let repo = gix::init_bare(&tmp_path)?;
println!("{}repo: {} (on-disk ODB){}", DIM, tmp_path.display(), RESET);

println!("\n{}generating {} commits…{}", BOLD, n_commits, RESET);
println!("\n{BOLD}generating {n_commits} commits…{RESET}");

let mut rng: u64 = 0xdeadbeef_cafebabe;
let gen = generate_history(&repo, n_commits, &mut rng)?;
Expand Down Expand Up @@ -498,7 +496,7 @@ pub fn run(n_commits: usize) -> Result<()> {
);

// Run git gc to pack loose objects (replaces the mempack flush).
print!("\n{}packing objects (git gc)…{}", BOLD, RESET);
print!("\n{BOLD}packing objects (git gc)…{RESET}");
let _ = std::io::stdout().flush();
let t_pack = Instant::now();

Expand All @@ -516,7 +514,7 @@ pub fn run(n_commits: usize) -> Result<()> {
if let Ok(entries) = std::fs::read_dir(&pack_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map(|e| e == "pack").unwrap_or(false) {
if path.extension().is_some_and(|e| e == "pack") {
pack_bytes += entry.metadata().map(|m| m.len()).unwrap_or(0);
}
}
Expand All @@ -536,7 +534,7 @@ pub fn run(n_commits: usize) -> Result<()> {
RESET,
);
} else {
println!(" {}skipped{} (git gc not available)", DIM, RESET);
println!(" {DIM}skipped{RESET} (git gc not available)");
}

// Point refs/meta/local at the tip commit.
Expand All @@ -547,7 +545,7 @@ pub fn run(n_commits: usize) -> Result<()> {
"history-walker: generation complete",
)?;

print!("\n{}walking history from tip…{}", BOLD, RESET);
print!("\n{BOLD}walking history from tip…{RESET}");
let _ = std::io::stdout().flush();

let walk = walk_history(&repo, tip_oid)?;
Expand All @@ -560,7 +558,7 @@ pub fn run(n_commits: usize) -> Result<()> {
fmt_ms(walk.elapsed_secs),
RESET
);
println!("\n{}walk results{}", BOLD, RESET);
println!("\n{BOLD}walk results{RESET}");
println!(
" {}commits visited{} {}{}{}",
DIM, RESET, CYAN, walk.commits_visited, RESET
Expand All @@ -578,17 +576,16 @@ pub fn run(n_commits: usize) -> Result<()> {
let expected = gen.total_shas_written;
let recovered = walk.shas_recovered;
let match_str = if recovered == expected {
format!("{}✓ exact match ({} SHAs){}", GREEN, recovered, RESET)
format!("{GREEN}✓ exact match ({recovered} SHAs){RESET}")
} else {
let diff = recovered as isize - expected as isize;
format!(
"{}✗ mismatch: expected {} got {} ({:+}) — check walk logic{}",
RED, expected, recovered, diff, RESET
"{RED}✗ mismatch: expected {expected} got {recovered} ({diff:+}) — check walk logic{RESET}"
)
};
println!(" {}correctness{} {}", DIM, RESET, match_str);
println!(" {DIM}correctness{RESET} {match_str}");

println!("\n{}timing summary{}", BOLD, RESET);
println!("\n{BOLD}timing summary{RESET}");
let gen_per_commit = gen.elapsed_secs / gen.commit_chain.len() as f64;
let walk_per_commit = walk.elapsed_secs / walk.commits_visited.max(1) as f64;
println!(
Expand Down
Loading
Loading