@@ -20,6 +20,7 @@ use std::ops::BitAndAssign;
2020use std:: ops:: BitOrAssign ;
2121use std:: ops:: BitXorAssign ;
2222use std:: ops:: SubAssign ;
23+ use std:: ptr;
2324
2425use databend_common_exception:: ErrorCode ;
2526use databend_common_exception:: Result ;
@@ -37,6 +38,10 @@ const HYBRID_HEADER_LEN: usize = 4;
3738
3839type SmallBitmap = SmallVec < [ u64 ; LARGE_THRESHOLD ] > ;
3940
41+ /// Perf Tips:
42+ /// - The deserialization performance of HybridBitmap significantly impacts the performance of Bitmap-related calculations.
43+ /// - Calculations may frequently create new Bitmaps; reusing them as much as possible can effectively improve performance.
44+ /// - do not use Box to construct HybridBitmap
4045#[ allow( clippy:: large_enum_variant) ]
4146#[ derive( Clone ) ]
4247pub enum HybridBitmap {
@@ -241,8 +246,7 @@ impl std::ops::BitOrAssign for HybridBitmap {
241246 }
242247 }
243248 HybridBitmap :: Small ( lhs_set) => {
244- let left = mem:: take ( lhs_set) ;
245- * lhs_set = small_union ( left, rhs_set. as_slice ( ) ) ;
249+ small_union ( lhs_set, rhs_set. as_slice ( ) ) ;
246250 if self . len ( ) >= LARGE_THRESHOLD as u64 {
247251 let _ = self . promote_to_tree ( ) ;
248252 }
@@ -303,14 +307,14 @@ impl std::ops::BitXorAssign for HybridBitmap {
303307 self . try_demote ( ) ;
304308 }
305309 HybridBitmap :: Small ( rhs_set) => match self {
310+ // Disjoint data in the bitmap can cause lhs_tree expansion during XOR, making this path a significant performance bottleneck.
306311 HybridBitmap :: Large ( lhs_tree) => {
307312 let rhs_tree = RoaringTreemap :: from_iter ( rhs_set. iter ( ) . copied ( ) ) ;
308313 lhs_tree. bitxor_assign ( rhs_tree) ;
309314 self . try_demote ( ) ;
310315 }
311316 HybridBitmap :: Small ( lhs_set) => {
312- let result = small_symmetric_difference ( lhs_set. as_slice ( ) , rhs_set. as_slice ( ) ) ;
313- * lhs_set = result;
317+ small_symmetric_difference ( lhs_set, rhs_set. as_slice ( ) ) ;
314318 if self . len ( ) >= LARGE_THRESHOLD as u64 {
315319 let _ = self . promote_to_tree ( ) ;
316320 }
@@ -541,12 +545,11 @@ fn decode_small_bitmap(payload: &[u8]) -> Result<HybridBitmap> {
541545 ) ) ) ;
542546 }
543547
544- let mut data = [ 0u8 ; std:: mem:: size_of :: < u64 > ( ) ] ;
545548 let set: SmallBitmap = bytes
546- . chunks_exact ( data . len ( ) )
547- . map ( move |chunk| {
548- data . copy_from_slice ( chunk) ;
549- u64:: from_le_bytes ( data )
549+ . chunks_exact ( std :: mem :: size_of :: < u64 > ( ) )
550+ . map ( |chunk| {
551+ let raw = unsafe { ptr :: read_unaligned ( chunk. as_ptr ( ) as * const u64 ) } ;
552+ u64:: from_le ( raw )
550553 } )
551554 . collect ( ) ;
552555 Ok ( HybridBitmap :: Small ( set) )
@@ -562,37 +565,62 @@ fn small_insert(set: &mut SmallBitmap, value: u64) -> bool {
562565 }
563566}
564567
565- fn small_union ( left : SmallBitmap , right : & [ u64 ] ) -> SmallBitmap {
566- if right . is_empty ( ) {
567- return left ;
568+ fn small_union ( target : & mut SmallBitmap , other : & [ u64 ] ) {
569+ if other . is_empty ( ) {
570+ return ;
568571 }
569- if left. is_empty ( ) {
570- return SmallBitmap :: from_slice ( right) ;
572+ if target. is_empty ( ) {
573+ target. extend_from_slice ( other) ;
574+ return ;
571575 }
572576
573- let left_slice = left. as_slice ( ) ;
574- let mut result = SmallBitmap :: with_capacity ( left_slice. len ( ) + right. len ( ) ) ;
575- let mut i = 0 ;
576- let mut j = 0 ;
577+ let lhs_len = target. len ( ) ;
578+ let rhs_len = other. len ( ) ;
579+ target. reserve ( rhs_len) ;
580+ let mut write = lhs_len + rhs_len;
581+ target. resize ( write, 0 ) ;
577582
578- while i < left_slice. len ( ) && j < right. len ( ) {
579- let lv = left_slice[ i] ;
580- let rv = right[ j] ;
581- if lv < rv {
582- result. push ( lv) ;
583- i += 1 ;
584- } else if rv < lv {
585- result. push ( rv) ;
586- j += 1 ;
587- } else {
588- result. push ( lv) ;
589- i += 1 ;
590- j += 1 ;
583+ let mut i = lhs_len;
584+ let mut j = rhs_len;
585+
586+ while i > 0 && j > 0 {
587+ let lv = target[ i - 1 ] ;
588+ let rv = other[ j - 1 ] ;
589+ write -= 1 ;
590+ match lv. cmp ( & rv) {
591+ std:: cmp:: Ordering :: Greater => {
592+ target[ write] = lv;
593+ i -= 1 ;
594+ }
595+ std:: cmp:: Ordering :: Less => {
596+ target[ write] = rv;
597+ j -= 1 ;
598+ }
599+ std:: cmp:: Ordering :: Equal => {
600+ target[ write] = lv;
601+ i -= 1 ;
602+ j -= 1 ;
603+ }
591604 }
592605 }
593- result. extend_from_slice ( & left_slice[ i..] ) ;
594- result. extend_from_slice ( & right[ j..] ) ;
595- result
606+
607+ while i > 0 {
608+ write -= 1 ;
609+ target[ write] = target[ i - 1 ] ;
610+ i -= 1 ;
611+ }
612+
613+ while j > 0 {
614+ write -= 1 ;
615+ target[ write] = other[ j - 1 ] ;
616+ j -= 1 ;
617+ }
618+
619+ if write > 0 {
620+ let len = target. len ( ) ;
621+ target. copy_within ( write..len, 0 ) ;
622+ target. truncate ( len - write) ;
623+ }
596624}
597625
598626fn small_intersection ( lhs : & mut SmallBitmap , rhs : & mut SmallBitmap ) {
@@ -673,28 +701,62 @@ fn small_difference(lhs: &[u64], rhs: &[u64]) -> SmallBitmap {
673701 result
674702}
675703
676- fn small_symmetric_difference ( lhs : & [ u64 ] , rhs : & [ u64 ] ) -> SmallBitmap {
677- let mut result = SmallBitmap :: with_capacity ( lhs. len ( ) + rhs. len ( ) ) ;
678- let mut i = 0 ;
679- let mut j = 0 ;
704+ fn small_symmetric_difference ( target : & mut SmallBitmap , other : & [ u64 ] ) {
705+ if other. is_empty ( ) {
706+ return ;
707+ }
708+ if target. is_empty ( ) {
709+ target. extend_from_slice ( other) ;
710+ return ;
711+ }
680712
681- while i < lhs. len ( ) && j < rhs. len ( ) {
682- let lv = lhs[ i] ;
683- let rv = rhs[ j] ;
684- if lv < rv {
685- result. push ( lv) ;
686- i += 1 ;
687- } else if rv < lv {
688- result. push ( rv) ;
689- j += 1 ;
690- } else {
691- i += 1 ;
692- j += 1 ;
713+ let lhs_len = target. len ( ) ;
714+ let rhs_len = other. len ( ) ;
715+ target. reserve ( rhs_len) ;
716+ let mut write = lhs_len + rhs_len;
717+ target. resize ( write, 0 ) ;
718+
719+ let mut i = lhs_len;
720+ let mut j = rhs_len;
721+
722+ while i > 0 && j > 0 {
723+ let lv = target[ i - 1 ] ;
724+ let rv = other[ j - 1 ] ;
725+ match lv. cmp ( & rv) {
726+ std:: cmp:: Ordering :: Greater => {
727+ write -= 1 ;
728+ target[ write] = lv;
729+ i -= 1 ;
730+ }
731+ std:: cmp:: Ordering :: Less => {
732+ write -= 1 ;
733+ target[ write] = rv;
734+ j -= 1 ;
735+ }
736+ std:: cmp:: Ordering :: Equal => {
737+ i -= 1 ;
738+ j -= 1 ;
739+ }
693740 }
694741 }
695- result. extend_from_slice ( & lhs[ i..] ) ;
696- result. extend_from_slice ( & rhs[ j..] ) ;
697- result
742+
743+ while i > 0 {
744+ write -= 1 ;
745+ target[ write] = target[ i - 1 ] ;
746+ i -= 1 ;
747+ }
748+
749+ while j > 0 {
750+ write -= 1 ;
751+ target[ write] = other[ j - 1 ] ;
752+ j -= 1 ;
753+ }
754+
755+ if write > 0 {
756+ let len = target. len ( ) ;
757+ target. copy_within ( write..len, 0 ) ;
758+ target. truncate ( len - write) ;
759+ }
698760}
699761
700762fn small_is_superset ( lhs : & SmallBitmap , rhs : & SmallBitmap ) -> bool {
@@ -759,10 +821,10 @@ mod tests {
759821
760822 #[ test]
761823 fn small_union_merges_and_deduplicates ( ) {
762- let left: SmallBitmap = smallvec ! [ 1_u64 , 3 , 5 ] ;
824+ let mut left: SmallBitmap = smallvec ! [ 1_u64 , 3 , 5 ] ;
763825 let right = [ 0_u64 , 3 , 4 , 7 ] ;
764- let result = small_union ( left, & right) ;
765- assert_eq ! ( result . as_slice( ) , & [ 0 , 1 , 3 , 4 , 5 , 7 ] ) ;
826+ small_union ( & mut left, & right) ;
827+ assert_eq ! ( left . as_slice( ) , & [ 0 , 1 , 3 , 4 , 5 , 7 ] ) ;
766828 }
767829
768830 #[ test]
@@ -798,10 +860,10 @@ mod tests {
798860
799861 #[ test]
800862 fn small_symmetric_difference_handles_overlap ( ) {
801- let lhs = [ 1_u64 , 2 , 4 ] ;
863+ let mut lhs: SmallBitmap = smallvec ! [ 1_u64 , 2 , 4 ] ;
802864 let rhs = [ 2_u64 , 3 , 5 ] ;
803- let result = small_symmetric_difference ( & lhs, & rhs) ;
804- assert_eq ! ( result . as_slice( ) , & [ 1 , 3 , 4 , 5 ] ) ;
865+ small_symmetric_difference ( & mut lhs, & rhs) ;
866+ assert_eq ! ( lhs . as_slice( ) , & [ 1 , 3 , 4 , 5 ] ) ;
805867 }
806868
807869 #[ test]
0 commit comments