slow5lib 0.1.0

Rust re-implementation of slow5lib: read and write SLOW5/BLOW5 nanopore sequencing files
Documentation
/// Parallel decompression benchmarks (requires `rayon` feature).
///
/// Compares sequential vs parallel decompression on the same data.
/// I/O stays on one thread; decompression fans out across rayon's thread pool.
/// The disk is the ceiling: how quickly decompression can keep up with I/O.
///
/// Run:
///   cargo bench --bench parallel --features rayon
///   cargo bench --bench parallel --features rayon -- parallel_five_reads
use std::time::{Duration, Instant};

use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};
use rayon::prelude::*;
use slow5lib::Slow5Reader;

const FIVE_READS_ZSTD: &str = "tests/data/five_reads.blow5";
const SMALL_BLOW5: &str = "tests/data/small.blow5";

/// Sequential vs parallel decompression over the five-read BLOW5 file.
///
/// With only five records, parallel overhead often dominates. This
/// establishes a baseline and reveals the crossover point.
fn bench_parallel_five_reads(c: &mut Criterion) {
    let mut group = c.benchmark_group("parallel_five_reads");

    group.bench_function("sequential", |b| {
        b.iter(|| {
            let mut reader = Slow5Reader::open(black_box(FIVE_READS_ZSTD)).expect("open");
            let mut total = 0usize;
            for rec in reader.records() {
                total += rec.expect("record ok").raw_signal.len();
            }
            black_box(total)
        });
    });

    group.bench_function("par_bridge", |b| {
        b.iter(|| {
            let mut reader = Slow5Reader::open(black_box(FIVE_READS_ZSTD)).expect("open");
            let total: usize = reader
                .records_raw()
                .par_bridge()
                .map(|r| {
                    r.expect("raw record ok")
                        .decompress()
                        .expect("decompress")
                        .raw_signal
                        .len()
                })
                .sum();
            black_box(total)
        });
    });

    group.bench_function("par_records", |b| {
        b.iter(|| {
            let mut reader = Slow5Reader::open(black_box(FIVE_READS_ZSTD)).expect("open");
            let total: usize = reader
                .par_records()
                .map(|r| r.expect("record ok").raw_signal.len())
                .sum();
            black_box(total)
        });
    });

    group.finish();
}

/// Sequential vs parallel decompression over the large BLOW5 file.
///
/// At this scale, parallel decompression should outperform sequential.
/// Throughput is reported in bytes/sec (compressed file size).
fn bench_parallel_large(c: &mut Criterion) {
    let file_bytes = std::fs::metadata(SMALL_BLOW5).map(|m| m.len()).unwrap_or(0);

    let mut group = c.benchmark_group("parallel_large");
    group.sample_size(10);
    group.measurement_time(Duration::from_secs(120));
    group.throughput(Throughput::Bytes(file_bytes));

    group.bench_function("sequential", |b| {
        b.iter_custom(|iters| {
            let mut total = Duration::ZERO;
            for _ in 0..iters {
                let mut reader = Slow5Reader::open(SMALL_BLOW5).expect("open");
                let t0 = Instant::now();
                let mut samples = 0usize;
                for rec in reader.records() {
                    samples += rec.expect("record ok").raw_signal.len();
                }
                total += t0.elapsed();
                black_box(samples);
            }
            total
        });
    });

    group.bench_function("par_records", |b| {
        b.iter_custom(|iters| {
            let mut total = Duration::ZERO;
            for _ in 0..iters {
                let mut reader = Slow5Reader::open(SMALL_BLOW5).expect("open");
                let t0 = Instant::now();
                let samples: usize = reader
                    .par_records()
                    .map(|r| r.expect("record ok").raw_signal.len())
                    .sum();
                total += t0.elapsed();
                black_box(samples);
            }
            total
        });
    });

    group.finish();
}

criterion_group!(benches, bench_parallel_five_reads, bench_parallel_large);
criterion_main!(benches);