flowscope 0.25.1

Passive flow & session tracking for packet capture (runtime-free, cross-platform)
Documentation
//! `BufferedReassembler::segment` throughput benchmarks.
//!
//! Run with:
//!
//!     cargo bench --bench reassembler --features reassembler

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use flowscope::{BufferedReassembler, OverflowPolicy, Reassembler, StreamChunks, Timestamp};

fn bench_in_order_1500_uncapped(c: &mut Criterion) {
    let payload = vec![0u8; 1500];
    c.bench_function("reassembler/in_order_1500_uncapped", |b| {
        let mut r = BufferedReassembler::new();
        let mut seq = 0u32;
        b.iter(|| {
            r.segment(seq, &payload, Timestamp::default());
            seq = seq.wrapping_add(payload.len() as u32);
            // Drain periodically to keep the buffer bounded without
            // forcing reallocation on every iteration.
            if r.buffered_len() > 1_000_000 {
                black_box(r.take());
            }
        })
    });
}

fn bench_in_order_1500_capped_1m(c: &mut Criterion) {
    let payload = vec![0u8; 1500];
    c.bench_function("reassembler/in_order_1500_capped_1m", |b| {
        let mut r = BufferedReassembler::new().with_max_buffer(1 << 20);
        let mut seq = 0u32;
        b.iter(|| {
            r.segment(seq, &payload, Timestamp::default());
            seq = seq.wrapping_add(payload.len() as u32);
            if r.buffered_len() > (1 << 19) {
                black_box(r.take());
            }
        })
    });
}

fn bench_sliding_window_overflow(c: &mut Criterion) {
    // Cap = 100; every 1500-byte segment overflows. Exercises the
    // rotation path under SlidingWindow.
    let payload = vec![0u8; 1500];
    c.bench_function("reassembler/sliding_window_overflow", |b| {
        let mut r = BufferedReassembler::new().with_max_buffer(100);
        let mut seq = 0u32;
        b.iter(|| {
            r.segment(seq, &payload, Timestamp::default());
            seq = seq.wrapping_add(payload.len() as u32);
        })
    });
}

fn bench_gap_skips(c: &mut Criterion) {
    // Every segment lands ahead of the expected sequence number: the
    // hole is skipped, a gap recorded, and the segment delivered.
    // Drained each iteration, as a session engine would.
    let payload = vec![0u8; 1500];
    c.bench_function("reassembler/gap_skips", |b| {
        let mut r = BufferedReassembler::new();
        let mut out = StreamChunks::new();
        let mut seq = 0u32;
        b.iter(|| {
            r.segment(seq, &payload, Timestamp::default());
            seq = seq.wrapping_add(2 * payload.len() as u32);
            out.clear();
            r.drain_into(&mut out);
            black_box(out.gap_count());
        })
    });
}

fn bench_drop_flow_idle(c: &mut Criterion) {
    // Drop-flow + post-poison no-op path: hot-path cost for
    // already-poisoned reassemblers.
    let payload = vec![0u8; 1500];
    c.bench_function("reassembler/drop_flow_poisoned", |b| {
        let mut r = BufferedReassembler::new()
            .with_max_buffer(100)
            .with_overflow_policy(OverflowPolicy::DropFlow);
        // Trigger the poison once up-front.
        r.segment(0, &payload, Timestamp::default());
        assert!(r.is_stopped());
        let mut seq = 1500u32;
        b.iter(|| {
            r.segment(seq, &payload, Timestamp::default());
            seq = seq.wrapping_add(payload.len() as u32);
        })
    });
}

criterion_group!(
    benches,
    bench_in_order_1500_uncapped,
    bench_in_order_1500_capped_1m,
    bench_sliding_window_overflow,
    bench_gap_skips,
    bench_drop_flow_idle,
);
criterion_main!(benches);