//! Round-17 dedup + CardDAV CPU/alloc micro-pack (no Postgres). //! //! Same rule as ROUND2–16: each section is BEFORE (verbatim replica of the //! shipped-before shape) vs AFTER (verbatim replica of the shipped-after shape, //! or the shipped helper where reachable), with a byte/-value equivalence gate //! and a `GATE FAIL … rollback` check that exits non-zero if the AFTER arm //! fails to reduce allocations — the round's roll-back rule encoded into the //! benchmark. //! //! [D1] `DedupService::hash_chunk_sequence` (delta-commit verification) took //! `chunks: &[(String, u64)]` and fed the backend stream via //! `chunks.iter().cloned()` — re-allocating every chunk-hash String a //! second time, on top of the owned `Vec` the caller already built with //! `c.h.clone()`. Taking the `Vec` by value and `into_iter()`-ing it //! moves those Strings in: zero internal clones. //! [D2] The chunk-ingest loop (`store_from_stream`) allocated the 64-char hex //! hash String THREE times per chunk: `to_hex().to_string()`, then //! `chunk_hashes.push(hash.clone())`, then `session_seen.insert(hash //! .clone())` — the last dropped immediately on a duplicate. Keying the //! intra-upload dedup set on the raw 32-byte BLAKE3 digest (`[u8; 32]`, //! `Copy`, no heap) drops the set clone entirely, and moving the hex into //! `chunk_hashes` on the duplicate branch drops the manifest clone there: //! 3 → 2 allocs (new chunk) / 3 → 1 (duplicate), on the hottest write //! path in the dedup system. //! [V1] `contact_to_vcard` emitted every EMAIL/TEL/ADR `TYPE=` token via //! `ty.to_uppercase()` — one throw-away String per token per vCard. The //! `push_upper` helper writes the upper-cased chars straight into the //! vCard buffer: zero temporaries. //! //! Run: //! cargo run --release --features bench --example bench_round17_micro //! Tunables (env): BENCH_ITERS (100000), BENCH_CHUNKS (64), BENCH_DUP_RATIO (2) use std::alloc::{GlobalAlloc, Layout, System}; use std::collections::HashSet; use std::env; use std::fmt::Write as _; use std::hint::black_box; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::Instant; use bytes::Bytes; static ALLOC_CALLS: AtomicU64 = AtomicU64::new(0); struct CountingAlloc; unsafe impl GlobalAlloc for CountingAlloc { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { ALLOC_CALLS.fetch_add(1, Ordering::Relaxed); unsafe { System.alloc(layout) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { unsafe { System.dealloc(ptr, layout) } } unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { ALLOC_CALLS.fetch_add(1, Ordering::Relaxed); unsafe { System.realloc(ptr, layout, new_size) } } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { ALLOC_CALLS.fetch_add(1, Ordering::Relaxed); unsafe { System.alloc_zeroed(layout) } } } #[global_allocator] static GLOBAL: CountingAlloc = CountingAlloc; fn env_or(key: &str, default: T) -> T { env::var(key) .ok() .and_then(|v| v.parse().ok()) .unwrap_or(default) } struct Measured { wall_ns_per_op: f64, allocs_per_op: f64, } fn measure(iters: usize, mut f: F) -> Measured { let a0 = ALLOC_CALLS.load(Ordering::Relaxed); let t = Instant::now(); for _ in 0..iters { f(); } let wall = t.elapsed().as_nanos() as f64 / iters as f64; let allocs = (ALLOC_CALLS.load(Ordering::Relaxed) - a0) as f64 / iters as f64; Measured { wall_ns_per_op: wall, allocs_per_op: allocs, } } fn print_row(label: &str, m: &Measured) { println!( "| {:<46} | {:>12.1} | {:>10.2} |", label, m.wall_ns_per_op, m.allocs_per_op ); } fn header_footer(name: &str, before: &Measured, after: &Measured) { println!("| arm | ns/op | allocs/op |"); print_row(&format!("BEFORE {name}"), before); print_row(&format!("AFTER {name}"), after); println!( "# {:.2}x wall, {:.2} fewer allocs/op", before.wall_ns_per_op / after.wall_ns_per_op, before.allocs_per_op - after.allocs_per_op ); } fn gate_allocs(tag: &str, before: &Measured, after: &Measured) { if after.allocs_per_op >= before.allocs_per_op { eprintln!("GATE FAIL [{tag}]: AFTER did not reduce allocations — rollback"); std::process::exit(1); } } // ──────────────────────────────────────────────────────────────────────────── // [D1] hash_chunk_sequence — `&[..]` + iter().cloned() vs `Vec` by value // ──────────────────────────────────────────────────────────────────────────── // // The caller (`delta_upload_service::commit`) already owns a fresh // `Vec<(String, u64)>` built with `c.h.clone()`; that `.collect()` is identical // on both call shapes, so it is EXCLUDED from the comparison. The delta is what // `hash_chunk_sequence` does INTERNALLY to feed `futures::stream::iter(..)`: // BEFORE — `chunks.iter().cloned()` re-clones every (String, u64) → N String // allocations (+ the collected Vec) inside the function. // AFTER — the `Vec` is moved in and `into_iter()`- d → the Strings relocate // with zero heap traffic; the function iterates the owned pairs. // The streamed (hash, size) pairs are byte-identical, so the recomputed BLAKE3 // and every size check are unchanged — only the ownership differs. fn d1_before_internal(chunks: &[(String, u64)]) -> Vec<(String, u64)> { // Materialises `stream::iter(chunks.iter().cloned())`'s input — the same N // element clones + one Vec the old `&[..]` signature forced. `to_vec()` is // `iter().cloned().collect()` (identical allocations), spelled the way // clippy prefers. chunks.to_vec() } fn section_hash_chunk_sequence() { let iters: usize = env_or("BENCH_ITERS", 100_000); let n: usize = env_or("BENCH_CHUNKS", 64); // A realistic manifest: N distinct 64-hex chunk hashes + declared sizes. let base: Vec<(String, u64)> = (0..n) .map(|i| { let h = blake3::hash(format!("d1-chunk-{i}").as_bytes()) .to_hex() .to_string(); (h, 1024 + i as u64) }) .collect(); // Gate: the old internal clone is a pure copy — moving instead changes // nothing the function observes (same pairs, same order). assert_eq!( d1_before_internal(&base), base, "d1 clone is not a pure copy" ); let before = measure(iters, || { // The internal re-clone the `&[..]` signature forced. black_box(d1_before_internal(black_box(&base))); }); let after = measure(iters, || { // The by-value signature adds no internal copy — it consumes the moved // pairs (modelled here as an in-order read of the same owned pairs). for c in black_box(&base).iter() { black_box(c); } }); println!("\n## [D1] hash_chunk_sequence internal clone ({n} chunks/op)"); header_footer("hash_chunk_sequence by-value", &before, &after); gate_allocs("D1", &before, &after); } // ──────────────────────────────────────────────────────────────────────────── // [D2] chunk-ingest loop — 3 hash-String allocs/chunk vs 2 (new) / 1 (dup) // ──────────────────────────────────────────────────────────────────────────── struct IngestOut { chunk_hashes: Vec, chunk_sizes: Vec, /// The distinct hashes that would be written to the backend, in order. pending: Vec, } /// BEFORE: verbatim replica of the shipped-before loop body. fn d2_before(payloads: &[Bytes]) -> IngestOut { let mut chunk_hashes: Vec = Vec::new(); let mut chunk_sizes: Vec = Vec::new(); let mut session_seen: HashSet = HashSet::new(); let mut pending: Vec<(String, Bytes)> = Vec::new(); for data in payloads { let hash = blake3::hash(data).to_hex().to_string(); chunk_sizes.push(data.len() as u64); chunk_hashes.push(hash.clone()); if session_seen.insert(hash.clone()) { pending.push((hash, data.clone())); } } IngestOut { chunk_hashes, chunk_sizes, pending: pending.into_iter().map(|(h, _)| h).collect(), } } /// AFTER: verbatim replica of the shipped-after loop body — the dedup set keys /// on the raw 32-byte digest, and the manifest push is split across the /// new/duplicate branches so a duplicate moves (not clones) the hex in. fn d2_after(payloads: &[Bytes]) -> IngestOut { let mut chunk_hashes: Vec = Vec::new(); let mut chunk_sizes: Vec = Vec::new(); let mut session_seen: HashSet<[u8; 32]> = HashSet::new(); let mut pending: Vec<(String, Bytes)> = Vec::new(); for data in payloads { let digest = blake3::hash(data); let hash = digest.to_hex().to_string(); chunk_sizes.push(data.len() as u64); if session_seen.insert(*digest.as_bytes()) { chunk_hashes.push(hash.clone()); pending.push((hash, data.clone())); } else { chunk_hashes.push(hash); } } IngestOut { chunk_hashes, chunk_sizes, pending: pending.into_iter().map(|(h, _)| h).collect(), } } fn section_chunk_ingest() { let iters: usize = env_or("BENCH_ITERS", 100_000); let n: usize = env_or("BENCH_CHUNKS", 64); // 1-in-K chunks repeats an earlier one (models intra-file dedup: repeated // blocks, zero-padded regions, re-chunked near-duplicates). K=2 ⇒ ~half the // stream is duplicate, the case a dedup store exists to make cheap. let dup_ratio: usize = env_or("BENCH_DUP_RATIO", 2).max(1); let payloads: Vec = (0..n) .map(|i| { let key = if dup_ratio > 0 && i % dup_ratio == 0 && i >= dup_ratio { i - dup_ratio // repeat an earlier chunk's bytes } else { i }; Bytes::from(format!("d2-chunk-payload-{key}-{}", "x".repeat(256))) }) .collect(); // Gate: identical observable output — the ordered manifest, the sizes, and // the distinct write set are byte-for-byte equal (only the private set's key // representation differs). let b = d2_before(&payloads); let a = d2_after(&payloads); assert_eq!(b.chunk_hashes, a.chunk_hashes, "d2 manifest differs"); assert_eq!(b.chunk_sizes, a.chunk_sizes, "d2 sizes differ"); assert_eq!(b.pending, a.pending, "d2 write-set differs"); let before = measure(iters, || { black_box(d2_before(black_box(&payloads))); }); let after = measure(iters, || { black_box(d2_after(black_box(&payloads))); }); println!("\n## [D2] chunk-ingest hash allocs ({n} chunks/op, 1-in-{dup_ratio} dup)"); header_footer("chunk-ingest session_seen [u8;32]", &before, &after); gate_allocs("D2", &before, &after); } // ──────────────────────────────────────────────────────────────────────────── // [V1] contact_to_vcard TYPE tokens — per-token to_uppercase() String vs push // ──────────────────────────────────────────────────────────────────────────── /// AFTER helper: write the upper-cased form of `s` straight into `buf`. /// Uses `char::to_uppercase`, so the bytes are identical to `s.to_uppercase()`. fn push_upper(buf: &mut String, s: &str) { for c in s.chars() { for u in c.to_uppercase() { buf.push(u); } } } /// BEFORE: verbatim replica — `write!` the `to_uppercase()` temporary. fn v1_before(types: &[&str]) -> String { let mut vcard = String::from("BEGIN:VCARD\r\nVERSION:3.0\r\n"); for ty in types { let _ = write!(vcard, "EMAIL;TYPE={}:x@e.test\r\n", ty.to_uppercase()); } vcard } /// AFTER: verbatim replica of the shipped-after emit — push pieces + upper. fn v1_after(types: &[&str]) -> String { let mut vcard = String::from("BEGIN:VCARD\r\nVERSION:3.0\r\n"); for ty in types { vcard.push_str("EMAIL;TYPE="); push_upper(&mut vcard, ty); vcard.push_str(":x@e.test\r\n"); } vcard } fn section_vcard_types() { let iters: usize = env_or("BENCH_ITERS", 100_000); // A contact's worth of EMAIL/TEL/ADR type tokens (already-upper, lower, // mixed, and an x- extension — the shapes real address books carry). let types = [ "HOME", "work", "Cell", "voice", "fax", "x-custom", "WORK", "home", ]; // Gate: byte-identical vCard, and push_upper == str::to_uppercase per token. for ty in types { let mut got = String::new(); push_upper(&mut got, ty); assert_eq!(got, ty.to_uppercase(), "push_upper differs for {ty:?}"); } assert_eq!(v1_before(&types), v1_after(&types), "v1 vcard differs"); let before = measure(iters, || { black_box(v1_before(black_box(&types))); }); let after = measure(iters, || { black_box(v1_after(black_box(&types))); }); println!( "\n## [V1] contact_to_vcard TYPE tokens ({} tokens/op)", types.len() ); header_footer("vcard TYPE push_upper", &before, &after); gate_allocs("V1", &before, &after); } fn main() { println!("#################################################################"); println!("# Round-17 dedup + CardDAV CPU/alloc micro-pack"); println!("#################################################################"); section_hash_chunk_sequence(); section_chunk_ingest(); section_vcard_types(); println!("\nGATE PASS (all sections)"); }