@@ -2,12 +2,17 @@ use bytes::Bytes;
22use criterion:: { BenchmarkId , Criterion , criterion_group, criterion_main} ;
33use rand:: seq:: index:: { self , IndexVec } ;
44use roaring:: RoaringBitmap ;
5- use std:: hint:: black_box;
5+ use std:: {
6+ hint:: black_box,
7+ ops:: { BitAnd , BitOr , BitXor , Sub } ,
8+ } ;
69
710use splinter_rs:: {
8- Optimizable , PartitionRead , PartitionWrite , Splinter , SplinterRef , testutil:: SetGen ,
11+ Cut , Optimizable , PartitionRead , PartitionWrite , Splinter , SplinterRef , testutil:: SetGen ,
912} ;
1013
14+ const SEED : u64 = 0xDEAD_BEEF ;
15+
1116fn mksplinter ( values : impl IntoIterator < Item = u32 > ) -> Splinter {
1217 Splinter :: from_iter ( values)
1318}
@@ -16,6 +21,10 @@ fn mksplinter_ref(values: impl IntoIterator<Item = u32>) -> SplinterRef<Bytes> {
1621 mksplinter ( values) . encode_to_splinter_ref ( )
1722}
1823
24+ fn mkroaring ( values : impl IntoIterator < Item = u32 > ) -> RoaringBitmap {
25+ values. into_iter ( ) . collect ( )
26+ }
27+
1928fn benchmark_contains ( c : & mut Criterion ) {
2029 let cardinalities = [ 4u32 , 16 , 64 , 256 , 1024 , 4096 , 16384 ] ;
2130
@@ -462,6 +471,145 @@ fn benchmark_select(c: &mut Criterion) {
462471 group. finish ( ) ;
463472}
464473
474+ macro_rules! bench_bitop {
475+ ( $name: ident, $group: literal, $op: path) => {
476+ fn $name( c: & mut Criterion ) {
477+ let cardinalities_left = [ 64 , 4096 , 16384 ] ;
478+ let cardinalities_right = [ 64 , 4096 , 16384 ] ;
479+
480+ let mut group = c. benchmark_group( $group) ;
481+ let mut set_gen = SetGen :: new( SEED ) ;
482+
483+ for & cardinality_left in & cardinalities_left {
484+ for & cardinality_right in & cardinalities_right {
485+ let set_a = set_gen. random( cardinality_left as usize ) ;
486+ let set_b = set_gen. random( cardinality_right as usize ) ;
487+
488+ group. bench_function(
489+ BenchmarkId :: new(
490+ "splinter" ,
491+ format!( "{cardinality_left}/{cardinality_right}" ) ,
492+ ) ,
493+ |b| {
494+ b. iter_batched(
495+ || ( mksplinter( set_a. clone( ) ) , mksplinter( set_b. clone( ) ) ) ,
496+ |( a, b) | black_box( $op( a, b) ) ,
497+ criterion:: BatchSize :: SmallInput ,
498+ ) ;
499+ } ,
500+ ) ;
501+
502+ group. bench_function(
503+ BenchmarkId :: new(
504+ "splinter_ref" ,
505+ format!( "{cardinality_left}/{cardinality_right}" ) ,
506+ ) ,
507+ |b| {
508+ let b_ref = mksplinter_ref( set_b. clone( ) ) ;
509+ b. iter_batched(
510+ || mksplinter( set_a. clone( ) ) ,
511+ |a| black_box( $op( a, & b_ref) ) ,
512+ criterion:: BatchSize :: SmallInput ,
513+ ) ;
514+ } ,
515+ ) ;
516+
517+ group. bench_function(
518+ BenchmarkId :: new(
519+ "roaring" ,
520+ format!( "{cardinality_left}/{cardinality_right}" ) ,
521+ ) ,
522+ |b| {
523+ b. iter_batched(
524+ || ( mkroaring( set_a. clone( ) ) , mkroaring( set_b. clone( ) ) ) ,
525+ |( a, b) | black_box( $op( a, b) ) ,
526+ criterion:: BatchSize :: SmallInput ,
527+ ) ;
528+ } ,
529+ ) ;
530+ }
531+ }
532+
533+ group. finish( ) ;
534+ }
535+ } ;
536+ }
537+
538+ bench_bitop ! ( benchmark_bitor, "bitor" , BitOr :: bitor) ;
539+ bench_bitop ! ( benchmark_bitand, "bitand" , BitAnd :: bitand) ;
540+ bench_bitop ! ( benchmark_bitxor, "bitxor" , BitXor :: bitxor) ;
541+ bench_bitop ! ( benchmark_sub, "sub" , Sub :: sub) ;
542+
543+ fn benchmark_cut ( c : & mut Criterion ) {
544+ let cardinalities_left = [ 64 , 4096 , 16384 ] ;
545+ let cardinalities_right = [ 64 , 4096 , 16384 ] ;
546+
547+ let mut group = c. benchmark_group ( "cut" ) ;
548+ let mut set_gen = SetGen :: new ( SEED ) ;
549+
550+ for & cardinality_left in & cardinalities_left {
551+ for & cardinality_right in & cardinalities_right {
552+ let set_a = set_gen. random ( cardinality_left as usize ) ;
553+ let set_b = set_gen. random ( cardinality_right as usize ) ;
554+
555+ group. bench_function (
556+ BenchmarkId :: new (
557+ "splinter" ,
558+ format ! ( "{cardinality_left}/{cardinality_right}" ) ,
559+ ) ,
560+ |b| {
561+ let other = mksplinter ( set_b. clone ( ) ) ;
562+ b. iter_batched (
563+ || mksplinter ( set_a. clone ( ) ) ,
564+ |mut a| {
565+ let intersection = a. cut ( & other) ;
566+ black_box ( ( a, intersection) )
567+ } ,
568+ criterion:: BatchSize :: SmallInput ,
569+ ) ;
570+ } ,
571+ ) ;
572+
573+ group. bench_function (
574+ BenchmarkId :: new (
575+ "splinter_ref" ,
576+ format ! ( "{cardinality_left}/{cardinality_right}" ) ,
577+ ) ,
578+ |b| {
579+ let other_ref = mksplinter_ref ( set_b. clone ( ) ) ;
580+ b. iter_batched (
581+ || mksplinter ( set_a. clone ( ) ) ,
582+ |mut a| {
583+ let intersection = a. cut ( & other_ref) ;
584+ black_box ( ( a, intersection) )
585+ } ,
586+ criterion:: BatchSize :: SmallInput ,
587+ ) ;
588+ } ,
589+ ) ;
590+
591+ // Equivalent roaring operation: intersection + difference
592+ group. bench_function (
593+ BenchmarkId :: new ( "roaring" , format ! ( "{cardinality_left}/{cardinality_right}" ) ) ,
594+ |b| {
595+ let other = mkroaring ( set_b. clone ( ) ) ;
596+ b. iter_batched (
597+ || mkroaring ( set_a. clone ( ) ) ,
598+ |mut a| {
599+ let intersection = & a & & other;
600+ a -= & other;
601+ black_box ( ( a, intersection) )
602+ } ,
603+ criterion:: BatchSize :: SmallInput ,
604+ ) ;
605+ } ,
606+ ) ;
607+ }
608+ }
609+
610+ group. finish ( ) ;
611+ }
612+
465613criterion_group ! (
466614 benches,
467615 benchmark_contains,
@@ -470,6 +618,11 @@ criterion_group!(
470618 benchmark_contains_vs_position,
471619 benchmark_cardinality,
472620 benchmark_rank,
473- benchmark_select
621+ benchmark_select,
622+ benchmark_bitor,
623+ benchmark_bitand,
624+ benchmark_bitxor,
625+ benchmark_sub,
626+ benchmark_cut
474627) ;
475628criterion_main ! ( benches) ;
0 commit comments