3838//! `Float32` and a `UInt32` both use four bytes per value. The filter compares
3939//! those stored bits through an unsigned type of the same size, without copying
4040//! the value buffer. A bit pattern is simply the bytes Arrow uses to store a
41- //! value. Float comparisons treat `0.0` and `-0.0` as equal under SQL
42- //! semantics, while preserving distinctions between different NaN values.
43- //! [`BranchlessFilterType`] defines these safe,
41+ //! value. Comparing it preserves details such as `0.0` versus `-0.0` and
42+ //! different NaN values. [`BranchlessFilterType`] defines these safe,
4443//! same-sized mappings and checks their sizes at compile time.
4544//!
4645//! The fast path is limited to short lists:
@@ -76,7 +75,6 @@ use arrow::buffer::{BooleanBuffer, ScalarBuffer};
7675use arrow:: datatypes:: * ;
7776use arrow:: util:: bit_iterator:: BitIndexIterator ;
7877use datafusion_common:: { Result , exec_datafusion_err, internal_datafusion_err} ;
79- use half:: f16;
8078
8179use super :: result:: build_result_from_contains;
8280use super :: static_filter:: StaticFilter ;
@@ -119,24 +117,20 @@ const BRANCHLESS_MAX_16B: usize = 4;
119117///
120118/// `T` is the logical Arrow type accepted by the filter. `CompareType` is the
121119/// same-width type used for the fixed comparison chain. Signed integers,
122- /// floats, and temporal values use an unsigned comparison type. Most values
123- /// compare by raw bit pattern; floats additionally treat both signed-zero
124- /// patterns as equal.
120+ /// floats, and temporal values use an unsigned comparison type so they compare
121+ /// by their raw bit pattern.
125122pub ( super ) trait BranchlessFilterType :
126- ArrowPrimitiveType + Send + Sync + Sized + ' static
123+ ArrowPrimitiveType + Send + Sync + ' static
127124{
128125 type CompareType : ArrowPrimitiveType + Send + Sync + ' static ;
129126
130127 /// Maximum number of non-null IN-list values to handle with
131128 /// [`BranchlessFilter`] for this primitive type.
132129 const MAX_LIST_LEN : usize ;
133-
134- /// The two signed-zero encodings for float types.
135- const SIGNED_ZERO_BITS : Option < [ BranchlessNative < Self > ; 2 ] > = None ;
136130}
137131
138132macro_rules! branchless_filter_type {
139- ( $logical: ty, $compare: ty, $max_len: expr $ ( , $zero_bits : expr ) ? ) => {
133+ ( $logical: ty, $compare: ty, $max_len: expr) => {
140134 // The branchless filter reads the same Arrow value buffer as the
141135 // comparison type. That is only valid when both native types have the
142136 // same width, so catch any bad mapping here at compile time.
@@ -149,7 +143,6 @@ macro_rules! branchless_filter_type {
149143 impl BranchlessFilterType for $logical {
150144 type CompareType = $compare;
151145 const MAX_LIST_LEN : usize = $max_len;
152- $( const SIGNED_ZERO_BITS : Option <[ BranchlessNative <Self >; 2 ] > = Some ( $zero_bits) ; ) ?
153146 }
154147 } ;
155148}
@@ -158,33 +151,18 @@ branchless_filter_type!(Int8Type, UInt8Type, BRANCHLESS_MAX_1B);
158151branchless_filter_type ! ( UInt8Type , UInt8Type , BRANCHLESS_MAX_1B ) ;
159152branchless_filter_type ! ( Int16Type , UInt16Type , BRANCHLESS_MAX_2B ) ;
160153branchless_filter_type ! ( UInt16Type , UInt16Type , BRANCHLESS_MAX_2B ) ;
161- branchless_filter_type ! (
162- Float16Type ,
163- UInt16Type ,
164- BRANCHLESS_MAX_2B ,
165- [ f16:: ZERO . to_bits( ) , f16:: NEG_ZERO . to_bits( ) ]
166- ) ;
154+ branchless_filter_type ! ( Float16Type , UInt16Type , BRANCHLESS_MAX_2B ) ;
167155
168156branchless_filter_type ! ( Int32Type , UInt32Type , BRANCHLESS_MAX_4B ) ;
169157branchless_filter_type ! ( UInt32Type , UInt32Type , BRANCHLESS_MAX_4B ) ;
170- branchless_filter_type ! (
171- Float32Type ,
172- UInt32Type ,
173- BRANCHLESS_MAX_4B ,
174- [ 0.0_f32 . to_bits( ) , ( -0.0_f32 ) . to_bits( ) ]
175- ) ;
158+ branchless_filter_type ! ( Float32Type , UInt32Type , BRANCHLESS_MAX_4B ) ;
176159branchless_filter_type ! ( Date32Type , UInt32Type , BRANCHLESS_MAX_4B ) ;
177160branchless_filter_type ! ( Time32SecondType , UInt32Type , BRANCHLESS_MAX_4B ) ;
178161branchless_filter_type ! ( Time32MillisecondType , UInt32Type , BRANCHLESS_MAX_4B ) ;
179162
180163branchless_filter_type ! ( Int64Type , UInt64Type , BRANCHLESS_MAX_8B ) ;
181164branchless_filter_type ! ( UInt64Type , UInt64Type , BRANCHLESS_MAX_8B ) ;
182- branchless_filter_type ! (
183- Float64Type ,
184- UInt64Type ,
185- BRANCHLESS_MAX_8B ,
186- [ 0.0_f64 . to_bits( ) , ( -0.0_f64 ) . to_bits( ) ]
187- ) ;
165+ branchless_filter_type ! ( Float64Type , UInt64Type , BRANCHLESS_MAX_8B ) ;
188166branchless_filter_type ! ( Date64Type , UInt64Type , BRANCHLESS_MAX_8B ) ;
189167branchless_filter_type ! ( Time64MicrosecondType , UInt64Type , BRANCHLESS_MAX_8B ) ;
190168branchless_filter_type ! ( Time64NanosecondType , UInt64Type , BRANCHLESS_MAX_8B ) ;
@@ -240,10 +218,8 @@ where
240218 }
241219
242220 let all_values = branchless_values :: < T > ( in_array) ;
243- // Float zero may add its other signed encoding.
244- let mut in_list_values = Vec :: with_capacity (
245- non_null_count + usize:: from ( T :: SIGNED_ZERO_BITS . is_some ( ) ) ,
246- ) ;
221+ let mut in_list_values = Vec :: with_capacity ( non_null_count) ;
222+
247223 match in_array. nulls ( ) {
248224 None => {
249225 in_list_values. extend ( all_values. iter ( ) . copied ( ) ) ;
@@ -258,18 +234,6 @@ where
258234 }
259235
260236 debug_assert_eq ! ( in_list_values. len( ) , non_null_count) ;
261-
262- // Add the other signed zero once so per-row lookup stays branch-free.
263- if let Some ( [ positive_zero, negative_zero] ) = T :: SIGNED_ZERO_BITS {
264- match (
265- in_list_values. contains ( & positive_zero) ,
266- in_list_values. contains ( & negative_zero) ,
267- ) {
268- ( true , false ) => in_list_values. push ( negative_zero) ,
269- ( false , true ) => in_list_values. push ( positive_zero) ,
270- _ => { }
271- }
272- }
273237 let in_list_values = in_list_values. into_boxed_slice ( ) ;
274238 let check_values = membership_check_for_len :: < T > ( in_list_values. len ( ) ) ;
275239
@@ -326,23 +290,13 @@ where
326290 T : BranchlessFilterType ,
327291 BranchlessNative < T > : Copy + PartialEq ,
328292{
329- /// Selects a fixed-size comparison function for the enclosing `len` and `T`.
330- ///
331- /// Arguments must enumerate every length from zero through the logical limit.
332- /// Their count is therefore one past that limit: the extra length supported
333- /// for floats when adding the other signed-zero encoding.
334- ///
335- /// The function is selected once during filter construction.
336293 macro_rules! choose {
337- ( $( $n: literal) ,+ $( , ) ?) => { {
338- const EXTRA_LEN : usize = [ $( $n) ,+] . len( ) ;
294+ ( $( $n: literal) ,* $( , ) ?) => {
339295 match len {
340- $( $n => check_values:: <BranchlessNative <T >, $n>, ) +
341- EXTRA_LEN if T :: SIGNED_ZERO_BITS . is_some( ) =>
342- check_values:: <BranchlessNative <T >, EXTRA_LEN >,
296+ $( $n => check_values:: <BranchlessNative <T >, $n>, ) *
343297 _ => unreachable!( "list length exceeds the configured limit" ) ,
344298 }
345- } } ;
299+ } ;
346300 }
347301
348302 // Avoid creating checks for lengths a type does not support.
@@ -497,11 +451,11 @@ mod tests {
497451
498452 assert_eq ! (
499453 filter. contains( & needles, false ) ?,
500- BooleanArray :: from( vec![ Some ( true ) , Some ( true ) , Some ( true ) , None , None ] )
454+ BooleanArray :: from( vec![ None , Some ( true ) , Some ( true ) , None , None ] )
501455 ) ;
502456 assert_eq ! (
503457 filter. contains( & needles, true ) ?,
504- BooleanArray :: from( vec![ Some ( false ) , Some ( false ) , Some ( false ) , None , None ] )
458+ BooleanArray :: from( vec![ None , Some ( false ) , Some ( false ) , None , None ] )
505459 ) ;
506460
507461 let wrong_type = UInt16Array :: from ( vec ! [ Some ( 0x8000 ) , Some ( 0x7e01 ) ] ) ;
@@ -512,26 +466,20 @@ mod tests {
512466 }
513467
514468 #[ test]
515- fn branchless_filter_floats_use_sql_zero_equality ( ) -> Result < ( ) > {
469+ fn branchless_filter_floats_use_bit_equality ( ) -> Result < ( ) > {
516470 let nan_a = f32:: from_bits ( 0x7fc0_0001 ) ;
517471 let nan_b = f32:: from_bits ( 0x7fc0_0002 ) ;
518472 let haystack: ArrayRef =
519- Arc :: new ( Float32Array :: from ( vec ! [ Some ( 0.0 ) , Some ( nan_a) ] ) ) ;
473+ Arc :: new ( Float32Array :: from ( vec ! [ Some ( - 0.0 ) , Some ( nan_a) ] ) ) ;
520474 let filter = BranchlessFilter :: < Float32Type > :: try_new ( & haystack) ?;
521475 let needles =
522476 Float32Array :: from ( vec ! [ Some ( 0.0 ) , Some ( -0.0 ) , Some ( nan_a) , Some ( nan_b) ] ) ;
523477
524478 assert_eq ! (
525479 filter. contains( & needles, false ) ?,
526- BooleanArray :: from( vec![ Some ( true ) , Some ( true ) , Some ( true ) , Some ( false ) ] )
480+ BooleanArray :: from( vec![ Some ( false ) , Some ( true ) , Some ( true ) , Some ( false ) ] )
527481 ) ;
528482
529- // A list containing both encodings is not expanded.
530- let zero_only: ArrayRef =
531- Arc :: new ( Float32Array :: from ( vec ! [ Some ( 0.0 ) , Some ( -0.0 ) ] ) ) ;
532- let filter = BranchlessFilter :: < Float32Type > :: try_new ( & zero_only) ?;
533- assert_eq ! ( filter. in_list_values. len( ) , 2 ) ;
534-
535483 let nan_a = f64:: from_bits ( 0x7ff8_0000_0000_0001 ) ;
536484 let nan_b = f64:: from_bits ( 0x7ff8_0000_0000_0002 ) ;
537485 let haystack: ArrayRef =
@@ -542,7 +490,7 @@ mod tests {
542490
543491 assert_eq ! (
544492 filter. contains( & needles, false ) ?,
545- BooleanArray :: from( vec![ Some ( true ) , Some ( true ) , Some ( true ) , Some ( false ) ] )
493+ BooleanArray :: from( vec![ Some ( false ) , Some ( true ) , Some ( true ) , Some ( false ) ] )
546494 ) ;
547495
548496 Ok ( ( ) )
0 commit comments