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

nordisk/znippy-zoomies

ljar/benches/chunk_into.rs

//! B6 before/after: the chunk-decode **output assembly** path.
//!
//! `decode_chunk` returns `Vec<Vec<u8>>` which the caller must
//! `flatten().collect()` — a fresh allocation + copy of the whole chunk every
//! call. `decode_chunk_into` (B6) decodes the segments in parallel into one
//! pre-allocated buffer at candidate offsets, then forward-seek compacts — and
//! the caller reuses the buffer across chunks, so the per-chunk output
//! allocation disappears. Stored blocks make the *decode* trivial so the
//! measurement isolates the assembly/allocation cost B6 targets.
//!
//! Run: `cargo bench -p ljar --bench chunk_into`

use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;
use ljar::chunk::{decode_chunk, decode_chunk_into};

fn stored_block(payload: &[u8], bfinal: bool) -> Vec<u8> {
    let mut out = Vec::with_capacity(payload.len() + 5);
    out.push(if bfinal { 0x01 } else { 0x00 });
    let len = payload.len() as u16;
    out.extend_from_slice(&len.to_le_bytes());
    out.extend_from_slice(&(!len).to_le_bytes());
    out.extend_from_slice(payload);
    out
}

/// `nblocks` non-final stored blocks of `block` bytes, each followed by a
/// Z_FULL_FLUSH marker, then one final block. Returns (stream, decoded_len).
fn build_stream(nblocks: usize, block: usize) -> (Vec<u8>, usize) {
    let payload = vec![0x5A_u8; block];
    let flush = [0x00u8, 0x00, 0x00, 0xFF, 0xFF];
    let mut buf = Vec::new();
    for _ in 0..nblocks {
        buf.extend_from_slice(&stored_block(&payload, false));
        buf.extend_from_slice(&flush);
    }
    buf.extend_from_slice(&stored_block(b"end", true));
    (buf, nblocks * block + 3)
}

fn bench(c: &mut Criterion) {
    let n_workers = std::thread::available_parallelism()
        .map(|n| n.get())
        .unwrap_or(8);
    let (buf, decoded) = build_stream(256, 16 * 1024); // ~4 MB decoded, many flush points

    let mut group = c.benchmark_group("chunk_into");
    group.throughput(Throughput::Bytes(decoded as u64));

    // BEFORE: Vec<Vec<u8>> + a fresh flatten().collect() per call.
    group.bench_function("decode_chunk_flatten", |b| {
        b.iter(|| {
            let (outs, _) = decode_chunk(black_box(&buf), n_workers, true).unwrap();
            let v: Vec<u8> = outs.into_iter().flatten().collect();
            black_box(v.len())
        })
    });

    // AFTER (B6): candidate-offset zero-copy decode into ONE reused buffer.
    group.bench_function("decode_chunk_into_reused", |b| {
        let mut out = Vec::with_capacity(decoded + (1 << 20));
        b.iter(|| {
            out.clear();
            decode_chunk_into(black_box(&buf), n_workers, true, &mut out).unwrap();
            black_box(out.len())
        })
    });

    group.finish();
}

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