Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy-zoomies

linflate/benches/inflate.rs

//! Throughput bench for the DEFLATE decode hot path (`inflate_into`).
//!
//! Three corpora exercise the match-copy distributions that drive `copy_match`:
//!   - `text`  : source-code-like, many short/medium back-refs (the common case)
//!   - `binary`: lower redundancy, longer literals, fewer matches
//!   - `rle`   : highly repetitive, long matches (stresses the 32-byte copy loop)
//! Each is compressed once with miniz_oxide level 6; the bench times linflate
//! decoding into a reused output buffer (no per-iter alloc). Throughput is over
//! the *uncompressed* output.

use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;

fn corpus_text(n: usize) -> Vec<u8> {
    // Repeated token soup with drift → realistic back-reference distances.
    let toks: &[&str] = &[
        "let mut ",
        "self.",
        "pub fn ",
        "return Ok(",
        "unsafe { ",
        "for i in 0..",
        "match x {",
        "println!(",
        "buffer",
        "_index",
        " = ",
        ";\n",
        "    ",
        "}\n",
        "compressed",
        "out_pos",
        "InflateError",
        "BitReader",
        "tables",
    ];
    let mut out = Vec::with_capacity(n);
    let mut s = 0x2545F4914F6CDD1Du64;
    while out.len() < n {
        s ^= s >> 12;
        s ^= s << 25;
        s ^= s >> 27;
        let t = toks[(s as usize) % toks.len()].as_bytes();
        out.extend_from_slice(t);
    }
    out.truncate(n);
    out
}

fn corpus_binary(n: usize) -> Vec<u8> {
    // Pseudo-random with occasional repeats → few, longer-distance matches.
    let mut out = Vec::with_capacity(n);
    let mut s = 0x9E3779B97F4A7C15u64;
    while out.len() < n {
        s ^= s >> 12;
        s ^= s << 25;
        s ^= s >> 27;
        let v = s.wrapping_mul(0x2545F4914F6CDD1D);
        out.extend_from_slice(&v.to_le_bytes());
        if (v & 0x3f) == 0 && out.len() > 64 {
            // splice a repeat to create a match
            let from = out.len() - 64;
            let win = out[from..from + 32].to_vec();
            out.extend_from_slice(&win);
        }
    }
    out.truncate(n);
    out
}

fn corpus_rle(n: usize) -> Vec<u8> {
    // Long runs → long matches, the 32-byte copy_chunks loop dominates.
    let mut out = Vec::with_capacity(n);
    let mut s = 1u64;
    while out.len() < n {
        s ^= s >> 12;
        s ^= s << 25;
        s ^= s >> 27;
        let b = (s & 0xff) as u8;
        let run = 16 + (s >> 8) as usize % 240;
        out.resize((out.len() + run).min(n), b);
    }
    out
}

fn bench(c: &mut Criterion) {
    let n = 4 * 1024 * 1024;
    let corpora: [(&str, Vec<u8>); 3] = [
        ("text", corpus_text(n)),
        ("binary", corpus_binary(n)),
        ("rle", corpus_rle(n)),
    ];

    let mut g = c.benchmark_group("inflate");
    for (name, raw) in &corpora {
        let comp = miniz_oxide::deflate::compress_to_vec(raw, 6);
        // sanity: linflate must reproduce it
        let check = linflate::inflate_to_vec(&comp, raw.len()).expect("decode");
        assert_eq!(&check, raw, "linflate roundtrip mismatch for {name}");

        let mut out = vec![0u8; raw.len() + 64]; // FASTLOOP headroom
        g.throughput(Throughput::Bytes(raw.len() as u64));
        g.bench_function(*name, |b| {
            b.iter(|| {
                let w = linflate::inflate_into(black_box(&comp), black_box(&mut out)).unwrap();
                black_box(w);
            });
        });
    }
    g.finish();
}

criterion_group!(benches, bench);
criterion_main!(benches);