benches/filter_bench.rs

//! Criterion benchmark for the filter pipeline.
//!
//! Run with `cargo bench --bench filter_bench`.

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

use ripgrab::filter::{dsl, FilterSet, MatchOutcome};

mod fixtures;

fn bench_basic_includes(c: &mut Criterion) {
    let lines = fixtures::access_log(fixtures::DEFAULT_LINES);
    let filter = FilterSet::compile(
        &["error|warn|fatal".to_string()],
        &["heartbeat".to_string()],
        None,
    )
    .unwrap();
    c.bench_function("filter/basic-includes", |b| {
        b.iter(|| {
            let mut hits = 0;
            for line in &lines {
                if let MatchOutcome::Plain = filter.apply(black_box(line)) {
                    hits += 1;
                }
            }
            black_box(hits)
        })
    });
}

fn bench_dsl_compiled(c: &mut Criterion) {
    let lines = fixtures::access_log(fixtures::DEFAULT_LINES);
    let compiled = dsl::parse_and_compile("status >= 400").unwrap();
    c.bench_function("filter/dsl-compiled", |b| {
        b.iter(|| {
            let mut ctx = dsl::EvalContext::new();
            let mut hits = 0;
            for line in &lines {
                let status = extract_status(line);
                ctx.clear();
                ctx.set("status", status);
                if dsl::evaluate(&compiled, &ctx) {
                    hits += 1;
                }
            }
            black_box(hits)
        })
    });
}

fn bench_dsl_by_line_length(c: &mut Criterion) {
    let mut group = c.benchmark_group("filter/dsl-line-length");
    for &len in &[128usize, 256, 512, 1024] {
        let line = format!("host=api status=503 body={}", "x".repeat(len));
        let compiled = dsl::parse_and_compile("status >= 400").unwrap();
        group.bench_with_input(BenchmarkId::from_parameter(len), &line, |b, l| {
            let mut ctx = dsl::EvalContext::new();
            b.iter(|| {
                ctx.clear();
                ctx.set("status", extract_status(l));
                black_box(dsl::evaluate(&compiled, &ctx));
            })
        });
    }
    group.finish();
}

fn extract_status(line: &str) -> &str {
    line.split_whitespace()
        .find_map(|f| f.strip_prefix("status="))
        .unwrap_or("0")
}

criterion_group!(benches, bench_basic_includes, bench_dsl_compiled, bench_dsl_by_line_length);
criterion_main!(benches);