use branchless_rust_benchmarks::{filter_branchless, filter_iter, filter_prealloc}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use rand::{RngExt, SeedableRng, rngs::StdRng}; use std::hint::black_box; const LEN: usize = 1_000_000; /// One million random `f64` values uniformly spread over `0.0..100.0`. fn random_input() -> Vec { let mut rng = StdRng::seed_from_u64(42); (0..LEN).map(|_| rng.random_range(0.0..100.0)).collect() } /// Every implementation at different selectivities. /// Since values are uniform over `0.0..100.0`, a threshold of 75.0 keeps ~25%. fn bench_selectivity(c: &mut Criterion) { let input = random_input(); let mut group = c.benchmark_group("filter"); for kept in [1u32, 25, 50, 75, 99] { let threshold = 100.0 - f64::from(kept); group.bench_with_input(BenchmarkId::new("iter", kept), &threshold, |b, &t| { b.iter(|| filter_iter(black_box(&input), black_box(t))) }); group.bench_with_input(BenchmarkId::new("prealloc", kept), &threshold, |b, &t| { b.iter(|| filter_prealloc(black_box(&input), black_box(t))) }); group.bench_with_input(BenchmarkId::new("branchless", kept), &threshold, |b, &t| { b.iter(|| filter_branchless(black_box(&input), black_box(t))) }); } group.finish(); } /// The same function, the same data, the same threshold. /// The only difference is the order of the elements. fn bench_sorted_vs_shuffled(c: &mut Criterion) { let shuffled = random_input(); let mut sorted = shuffled.clone(); sorted.sort_unstable_by(f64::total_cmp); let threshold = 50.0; let mut group = c.benchmark_group("sorted_vs_shuffled"); group.bench_function("shuffled", |b| { b.iter(|| filter_iter(black_box(&shuffled), black_box(threshold))) }); group.bench_function("sorted", |b| { b.iter(|| filter_iter(black_box(&sorted), black_box(threshold))) }); group.finish(); } criterion_group!(benches, bench_selectivity, bench_sorted_vs_shuffled); criterion_main!(benches);