Skip to main content

bench_stream/
bench_stream.rs

1//! Tiny release timing harness for the `compress_stream` path (gate 2) + a
2//! round-trip byte-identical check (gate 1). NOT part of the published crate;
3//! an example only. Run: `cargo run -p znippy-compress --release --example bench_stream`.
4
5use std::time::Instant;
6
7use znippy_compress::{compress_stream, ArchiveEntry};
8
9const MB: usize = 1024 * 1024;
10
11fn gen_text(size: usize) -> Vec<u8> {
12    // Mildly/highly compressible English-ish filler.
13    let word = b"the quick brown fox jumps over the lazy dog ";
14    let mut v = Vec::with_capacity(size);
15    while v.len() < size {
16        v.extend_from_slice(word);
17    }
18    v.truncate(size);
19    v
20}
21
22fn gen_random(size: usize) -> Vec<u8> {
23    // Deterministic xorshift => incompressible.
24    let mut s = 0x9E37_79B9_7F4A_7C15u64;
25    (0..size)
26        .map(|_| {
27            s ^= s >> 12;
28            s ^= s << 25;
29            s ^= s >> 27;
30            (s.wrapping_mul(0x2545_F491_4F6C_DD1D) >> 33) as u8
31        })
32        .collect()
33}
34
35fn build_entries(kind: &str) -> Vec<ArchiveEntry> {
36    match kind {
37        "text" => vec![ArchiveEntry::new("text.txt", gen_text(512 * MB))],
38        "random" => vec![ArchiveEntry::new("random.bin", gen_random(512 * MB))],
39        "mixed" => vec![
40            ArchiveEntry::new("pom.xml", gen_text(64 * 1024)),
41            ArchiveEntry::new("app.jar", gen_random(180 * MB)),
42            ArchiveEntry::new("sources.jar", gen_text(120 * MB)),
43            ArchiveEntry::new("javadoc.jar", gen_text(90 * MB)),
44            ArchiveEntry::new("deps.tar.gz", gen_random(140 * MB)),
45        ],
46        "small" => (0..2000)
47            .map(|i| ArchiveEntry::new(format!("f_{i:04}.txt"), gen_text(4096 + (i % 97))))
48            .collect(),
49        _ => panic!("unknown kind"),
50    }
51}
52
53fn run_once(kind: &str, entries: Vec<ArchiveEntry>, archive: &std::path::PathBuf) -> (f64, u64, u64) {
54    let input_bytes: u64 = entries.iter().map(|e| e.data.len() as u64).sum();
55    let t0 = Instant::now();
56    let compressor = compress_stream(archive, false).expect("compress_stream");
57    for e in entries {
58        compressor.sender().send(e).expect("send");
59    }
60    let report = compressor.finish().expect("finish");
61    let secs = t0.elapsed().as_secs_f64();
62    let _ = kind;
63    (secs, input_bytes, report.chunks)
64}
65
66fn main() {
67    let dir = std::env::var("BENCH_DIR").unwrap_or_else(|_| "/run/media/rickard/T9/tmp/bench_stream".into());
68    std::fs::create_dir_all(&dir).unwrap();
69
70    for kind in ["text", "random", "mixed", "small"] {
71        let archive = std::path::PathBuf::from(&dir).join(format!("{kind}.znippy"));
72        let mut best = f64::INFINITY;
73        let mut in_bytes = 0u64;
74        let mut chunks = 0u64;
75        for _ in 0..3 {
76            let entries = build_entries(kind);
77            let (secs, ib, ch) = run_once(kind, entries, &archive);
78            in_bytes = ib;
79            chunks = ch;
80            if secs < best {
81                best = secs;
82            }
83        }
84        let in_mb = in_bytes as f64 / MB as f64;
85        let mbps = in_mb / best;
86
87        // Round-trip verify (gate 1): decompress and compare bytes.
88        let arc = archive.with_extension("znippy");
89        let dec = std::path::PathBuf::from(&dir).join(format!("{kind}_dec"));
90        let _ = std::fs::remove_dir_all(&dec);
91        std::fs::create_dir_all(&dec).unwrap();
92        let v = znippy_common::decompress_archive(&arc, true, &dec).expect("decompress");
93        let orig = build_entries(kind);
94        let mut roundtrip_ok = v.corrupt_files == 0;
95        for e in &orig {
96            let p = dec.join(&e.relative_path);
97            match std::fs::read(&p) {
98                Ok(b) => {
99                    if b != e.data {
100                        roundtrip_ok = false;
101                        eprintln!("MISMATCH {kind} {}", e.relative_path);
102                    }
103                }
104                Err(err) => {
105                    roundtrip_ok = false;
106                    eprintln!("MISSING {kind} {}: {err}", e.relative_path);
107                }
108            }
109        }
110
111        println!(
112            "BENCH kind={kind} best_s={best:.4} in_mb={in_mb:.1} mbps={mbps:.1} chunks={chunks} corrupt={} roundtrip_ok={roundtrip_ok}",
113            v.corrupt_files
114        );
115    }
116}