lzip-parallel 0.2.3

Pure Rust parallel ZIP decompressor — multi-core DEFLATE decode for .zip archives
Documentation
//! Full ZIP decompression benchmark with wall-clock timing and core utilization.
//!
//! Downloads a real ZIP (JAR) from Maven Central (cached), then benchmarks:
//! - Full parallel decompress (all cores)
//! - Reports wall time, throughput, and effective core utilization.
//!
//! Run: cargo bench --bench zip_throughput
//!
//! Maven fixture set + downloader live in `nornir::bench::assets` (shared
//! with ljar).

use criterion::{criterion_group, criterion_main, Criterion, Throughput, BenchmarkId};
use nornir::bench::assets::{self, BENCH_JARS};
use std::fs;

fn bench_zip_decompress(c: &mut Criterion) {
    let mut group = c.benchmark_group("zip_decompress");
    group.sample_size(20);

    let cache = assets::default_cache_dir();
    for jar in BENCH_JARS {
        let path = match assets::ensure(jar, &cache) {
            Some(p) => p,
            None => {
                eprintln!("  skipping {} (download failed)", jar.filename);
                continue;
            }
        };

        let data = fs::read(&path).unwrap();
        let file_size = data.len() as u64;
        group.throughput(Throughput::Bytes(file_size));

        group.bench_with_input(
            BenchmarkId::new("lzip", jar.label),
            &data,
            |b, data| {
                b.iter(|| {
                    let entries = lzip_parallel::decompress_zip(data).unwrap();
                    criterion::black_box(entries.len())
                })
            },
        );
    }

    group.finish();
}

fn bench_lzip_vs_zip_crate(c: &mut Criterion) {
    let mut group = c.benchmark_group("jar_vs_zip_crate");
    group.sample_size(20);

    let cache = assets::default_cache_dir();
    let jar = &BENCH_JARS[0]; // guava
    let path = match assets::ensure(jar, &cache) {
        Some(p) => p,
        None => {
            eprintln!("  skipping comparison (download failed)");
            return;
        }
    };

    let data = fs::read(&path).unwrap();
    let file_size = data.len() as u64;
    group.throughput(Throughput::Bytes(file_size));

    group.bench_function("lzip", |b| {
        b.iter(|| {
            let entries = lzip_parallel::decompress_zip(&data).unwrap();
            criterion::black_box(entries.len())
        })
    });

    group.bench_function("zip_crate_sequential", |b| {
        b.iter(|| {
            use std::io::{Cursor, Read};
            let cursor = Cursor::new(&data);
            let mut archive = zip::ZipArchive::new(cursor).unwrap();
            let mut total = 0usize;
            for i in 0..archive.len() {
                let mut entry = archive.by_index(i).unwrap();
                if entry.is_dir() { continue; }
                let mut buf = Vec::with_capacity(entry.size() as usize);
                entry.read_to_end(&mut buf).unwrap();
                total += buf.len();
            }
            criterion::black_box(total)
        })
    });

    group.finish();
}

fn bench_core_scaling(_c: &mut Criterion) {
    // Core scaling requires re-creating the thread pool per iteration.
    // The OnceLock pool design makes this impractical in a benchmark.
    // Use `time lzip <file>` with LZIP_THREADS=N for manual scaling tests.
}

criterion_group!(benches, bench_zip_decompress, bench_lzip_vs_zip_crate);
criterion_main!(benches);