use criterion::{ criterion_group, criterion_main, measurement::WallTime, BatchSize, BenchmarkGroup, BenchmarkId, Criterion, }; use flat_bit_set::FlatBitSet; use litemap::LiteMap; use roaring::RoaringBitmap; use std::{ collections::{BTreeSet, HashSet}, fmt::Arguments, hint::black_box, }; type SparseBitSet = hi_sparse_bitset::BitSet; fn insert(c: &mut Criterion) { let mut g = c.benchmark_group("insert"); for n in [10, 100, 1000] { let mut data: Vec<_> = (0..n).collect(); test_insert(&mut g, &data, format_args!("dense {n}")); data.reverse(); test_insert(&mut g, &data, format_args!("dense rev {n}")); data.reverse(); data.iter_mut().for_each(|x| *x *= 100); test_insert(&mut g, &data, format_args!("sparse {n}")); data.reverse(); test_insert(&mut g, &data, format_args!("sparse rev {n}")); } g.finish(); } fn test_insert(g: &mut BenchmarkGroup, input: &[u32], p: Arguments) { g.bench_with_input(BenchmarkId::new("flat_bit_set", p), &input, |b, &input| { b.iter_batched( FlatBitSet::::new, |mut set| { for &e in input { set.insert(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("hashset", p), &input, |b, &input| { b.iter_batched( HashSet::::new, |mut set| { for &e in input { set.insert(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("btreeset", p), &input, |b, &input| { b.iter_batched( BTreeSet::::new, |mut set| { for &e in input { set.insert(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("litemap", p), &input, |b, &input| { b.iter_batched( LiteMap::::new, |mut set| { for &e in input { set.insert(e, ()); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("roaring", p), &input, |b, &input| { b.iter_batched( RoaringBitmap::new, |mut set| { for &e in input { set.insert(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input( BenchmarkId::new("hi_sparse_bitset", p), &input, |b, &input| { b.iter_batched( SparseBitSet::new, |mut set| { for &e in input { set.insert(e as usize); } set }, BatchSize::LargeInput, ); }, ); } fn remove(c: &mut Criterion) { let mut g = c.benchmark_group("remove"); for n in [10, 100, 1000] { let mut data: Vec<_> = (0..n).collect(); test_remove(&mut g, &data, format_args!("dense {n}")); data.reverse(); test_remove(&mut g, &data, format_args!("dense rev {n}")); data.reverse(); data.iter_mut().for_each(|x| *x *= 100); test_remove(&mut g, &data, format_args!("sparse {n}")); data.reverse(); test_remove(&mut g, &data, format_args!("sparse rev {n}")); } g.finish(); } fn test_remove(g: &mut BenchmarkGroup, input: &[u32], p: Arguments) { g.bench_with_input(BenchmarkId::new("flat_bit_set", p), &input, |b, &input| { let set: FlatBitSet = input.iter().copied().collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("hashset", p), &input, |b, &input| { let set: HashSet = input.iter().copied().collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(&e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("btreeset", p), &input, |b, &input| { let set: BTreeSet = input.iter().copied().collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(&e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("litemap", p), &input, |b, &input| { let set: LiteMap = input.iter().map(|&x| (x, ())).collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(&e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input(BenchmarkId::new("roaring", p), &input, |b, &input| { let set: RoaringBitmap = input.iter().copied().collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(e); } set }, BatchSize::LargeInput, ); }); g.bench_with_input( BenchmarkId::new("hi_sparse_bitset", p), &input, |b, &input| { let set: SparseBitSet = input.iter().map(|x| *x as usize).collect(); b.iter_batched( || set.clone(), |mut set| { for &e in input { set.remove(e as usize); } set }, BatchSize::LargeInput, ); }, ); } fn get(c: &mut Criterion) { let mut g = c.benchmark_group("get"); for n in [10, 100, 1000] { let mut data: Vec<_> = (0..n).collect(); test_get(&mut g, &data, format_args!("dense {n}")); data.iter_mut().for_each(|x| *x *= 100); test_get(&mut g, &data, format_args!("sparse {n}")); } g.finish(); } fn test_get(g: &mut BenchmarkGroup, input: &[u32], p: Arguments) { g.bench_with_input(BenchmarkId::new("flat_bit_set", p), &input, |b, &input| { let set: FlatBitSet = input.iter().copied().collect(); b.iter(|| { for &e in input { black_box(set.contains(e)); } }); }); g.bench_with_input(BenchmarkId::new("hashset", p), &input, |b, &input| { let set: HashSet = input.iter().copied().collect(); b.iter(|| { for &e in input { black_box(set.contains(&e)); } }); }); g.bench_with_input(BenchmarkId::new("btreeset", p), &input, |b, &input| { let set: BTreeSet = input.iter().copied().collect(); b.iter(|| { for &e in input { black_box(set.contains(&e)); } }); }); g.bench_with_input(BenchmarkId::new("litemap", p), &input, |b, &input| { let set: LiteMap = input.iter().map(|&x| (x, ())).collect(); b.iter(|| { for &e in input { black_box(set.get(&e).is_some()); } }); }); g.bench_with_input(BenchmarkId::new("roaring", p), &input, |b, &input| { let set: RoaringBitmap = input.iter().copied().collect(); b.iter(|| { for &e in input { black_box(set.contains(e)); } }); }); g.bench_with_input( BenchmarkId::new("hi_sparse_bitset", p), &input, |b, &input| { let set: SparseBitSet = input.iter().map(|x| *x as usize).collect(); b.iter(|| { for &e in input { black_box(set.contains(e as usize)); } }); }, ); } fn iter(c: &mut Criterion) { let mut g = c.benchmark_group("iter"); for n in [10, 100, 1000] { let mut data: Vec<_> = (0..n).collect(); test_iter(&mut g, &data, format_args!("dense {n}")); data.iter_mut().for_each(|x| *x *= 100); test_iter(&mut g, &data, format_args!("sparse {n}")); } g.finish(); } fn test_iter(g: &mut BenchmarkGroup, input: &[u32], p: Arguments) { g.bench_with_input(BenchmarkId::new("flat_bit_set", p), &input, |b, &input| { let set: FlatBitSet = input.iter().copied().collect(); b.iter(|| { set.iter().for_each(|x| { black_box(x); }); }); }); g.bench_with_input(BenchmarkId::new("hashset", p), &input, |b, &input| { let set: HashSet = input.iter().copied().collect(); b.iter(|| { set.iter().for_each(|&x| { black_box(x); }); }); }); g.bench_with_input(BenchmarkId::new("btreeset", p), &input, |b, &input| { let set: BTreeSet = input.iter().copied().collect(); b.iter(|| { set.iter().for_each(|&x| { black_box(x); }); }); }); g.bench_with_input(BenchmarkId::new("litemap", p), &input, |b, &input| { let set: LiteMap = input.iter().map(|&x| (x, ())).collect(); b.iter(|| { set.iter().for_each(|(&x, _)| { black_box(x); }); }); }); g.bench_with_input(BenchmarkId::new("roaring", p), &input, |b, &input| { let set: RoaringBitmap = input.iter().copied().collect(); b.iter(|| { set.iter().for_each(|x| { black_box(x); }); }); }); g.bench_with_input( BenchmarkId::new("hi_sparse_bitset", p), &input, |b, &input| { let set: SparseBitSet = input.iter().map(|x| *x as usize).collect(); b.iter(|| { set.iter().for_each(|x| { black_box(x); }); }); }, ); } fn biop_with(c: &mut Criterion) { let mut g = c.benchmark_group("biop_with"); for n in [10, 100, 1000] { let mut i1: Vec<_> = (0..n * 2).collect(); let mut i2: Vec<_> = (n..n * 3).collect(); test_biop_with(&mut g, &i1, &i2, format_args!("dense {n}")); i1.iter_mut().for_each(|x| *x *= 100); i2.iter_mut().for_each(|x| *x *= 100); test_biop_with(&mut g, &i1, &i2, format_args!("sparse {n}")); } g.finish(); } fn test_biop_with(g: &mut BenchmarkGroup, i1: &[u32], i2: &[u32], p: Arguments) { g.bench_with_input( BenchmarkId::new("flat_bit_set", p), &(i1, i2), |b, &(i1, i2)| { let s1: FlatBitSet = i1.iter().copied().collect(); let s2: FlatBitSet = i2.iter().copied().collect(); b.iter_batched( || (s1.clone(), s1.clone(), s1.clone(), s1.clone()), |(a, b, c, d)| (a & &s2, b | &s2, c ^ &s2, d - &s2), BatchSize::LargeInput, ); }, ); g.bench_with_input(BenchmarkId::new("roaring", p), &(i1, i2), |b, &(i1, i2)| { let s1: RoaringBitmap = i1.iter().copied().collect(); let s2: RoaringBitmap = i2.iter().copied().collect(); b.iter_batched( || (s1.clone(), s1.clone(), s1.clone(), s1.clone()), |(a, b, c, d)| (a & &s2, b | &s2, c ^ &s2, d - &s2), BatchSize::LargeInput, ); }); } fn biop_iter(c: &mut Criterion) { let mut g = c.benchmark_group("biop_iter"); for n in [10, 100, 1000] { let mut i1: Vec<_> = (0..n * 2).collect(); let mut i2: Vec<_> = (n..n * 3).collect(); test_biop_iter(&mut g, &i1, &i2, format_args!("dense {n}")); i1.iter_mut().for_each(|x| *x *= 100); i2.iter_mut().for_each(|x| *x *= 100); test_biop_iter(&mut g, &i1, &i2, format_args!("sparse {n}")); } g.finish(); } fn test_biop_iter(g: &mut BenchmarkGroup, i1: &[u32], i2: &[u32], p: Arguments) { g.bench_with_input( BenchmarkId::new("flat_bit_set", p), &(i1, i2), |b, &(i1, i2)| { let s1: FlatBitSet = i1.iter().copied().collect(); let s2: FlatBitSet = i2.iter().copied().collect(); b.iter(|| { for k in s1.intersection_iter(&s2) { black_box(k); } for k in s1.union_iter(&s2) { black_box(k); } for k in s1.symmetric_difference_iter(&s2) { black_box(k); } for k in s1.difference_iter(&s2) { black_box(k); } }); }, ); g.bench_with_input(BenchmarkId::new("hashset", p), &(i1, i2), |b, &(i1, i2)| { let s1: HashSet = i1.iter().copied().collect(); let s2: HashSet = i2.iter().copied().collect(); b.iter(|| { for k in s1.intersection(&s2) { black_box(k); } for k in s1.union(&s2) { black_box(k); } for k in s1.symmetric_difference(&s2) { black_box(k); } for k in s1.difference(&s2) { black_box(k); } }); }); g.bench_with_input( BenchmarkId::new("btreeset", p), &(i1, i2), |b, &(i1, i2)| { let s1: BTreeSet = i1.iter().copied().collect(); let s2: BTreeSet = i2.iter().copied().collect(); b.iter(|| { for k in s1.intersection(&s2) { black_box(k); } for k in s1.union(&s2) { black_box(k); } for k in s1.symmetric_difference(&s2) { black_box(k); } for k in s1.difference(&s2) { black_box(k); } }); }, ); } fn biop_new(c: &mut Criterion) { let mut g = c.benchmark_group("biop_new"); for n in [10, 100, 1000] { let mut i1: Vec<_> = (0..n * 2).collect(); let mut i2: Vec<_> = (n..n * 3).collect(); test_biop_new(&mut g, &i1, &i2, format_args!("dense {n}")); i1.iter_mut().for_each(|x| *x *= 100); i2.iter_mut().for_each(|x| *x *= 100); test_biop_new(&mut g, &i1, &i2, format_args!("sparse {n}")); } g.finish(); } fn test_biop_new(g: &mut BenchmarkGroup, i1: &[u32], i2: &[u32], p: Arguments) { g.bench_with_input( BenchmarkId::new("flat_bit_set", p), &(i1, i2), |b, &(i1, i2)| { let s1: FlatBitSet = i1.iter().copied().collect(); let s2: FlatBitSet = i2.iter().copied().collect(); b.iter_with_large_drop(|| (&s1 & &s2, &s1 | &s2, &s1 ^ &s2, &s1 - &s2)); }, ); g.bench_with_input(BenchmarkId::new("roaring", p), &(i1, i2), |b, &(i1, i2)| { let s1: RoaringBitmap = i1.iter().copied().collect(); let s2: RoaringBitmap = i2.iter().copied().collect(); b.iter_with_large_drop(|| (&s1 & &s2, &s1 | &s2, &s1 ^ &s2, &s1 - &s2)); }); } criterion_group!(benches, insert, remove, get, iter, biop_with, biop_iter, biop_new); criterion_main!(benches);