This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-11501-36122935e488001fae53344ed17ee39f0a5970c9 in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 0706d317a94098cec4b631031efcdf16f51ed1f5 Author: dhruv tanwar <[email protected]> AuthorDate: Sun Oct 11 20:57:05 2026 +0000 feat: specialize boolean interleave (#11501) ## Summary - Add a specialized Boolean interleave implementation that copies selected Boolean values directly into a `BooleanBufferBuilder`. - Preserve null handling using the existing `Interleave` helper. - Add tests covering mixed Boolean values, nulls, sliced arrays, and comparison against the specialized implementation's fallback. - Add Boolean interleave benchmark cases with and without nulls. ## Testing - `cargo fmt --check` - `cargo test -p arrow-select interleave` - `cargo test -p arrow-select` - `cargo bench -p arrow --bench interleave_kernels --features test_utils --no-run` ## Benchmark results Preliminary benchmarks showed improvements over the fallback implementation across the tested selection sizes, with approximate speedups of 1.7x–4.1x. These measurements came from separate runs and should be treated as indicative rather than a controlled A/B comparison. Closes #11379 --------- Co-authored-by: Jeffrey Vo <[email protected]> --- arrow-select/src/interleave.rs | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/arrow-select/src/interleave.rs b/arrow-select/src/interleave.rs index 74e29defe8..00abaf638e 100644 --- a/arrow-select/src/interleave.rs +++ b/arrow-select/src/interleave.rs @@ -101,6 +101,7 @@ pub fn interleave( downcast_primitive! { data_type => (primitive_helper, values, indices, data_type), + DataType::Boolean => interleave_boolean(values, indices), DataType::Utf8 => interleave_bytes::<Utf8Type>(values, indices), DataType::LargeUtf8 => interleave_bytes::<LargeUtf8Type>(values, indices), DataType::Binary => interleave_bytes::<BinaryType>(values, indices), @@ -220,6 +221,23 @@ fn interleave_primitive<T: ArrowPrimitiveType>( Ok(Arc::new(array.with_data_type(data_type.clone()))) } +fn interleave_boolean( + values: &[&dyn Array], + indices: &[(usize, usize)], +) -> Result<ArrayRef, ArrowError> { + let interleaved = Interleave::<'_, BooleanArray>::new(values, indices); + let arrays = &interleaved.arrays; + + let mut values_builder = BooleanBufferBuilder::new(indices.len()); + + for &(array_idx, row_idx) in indices { + values_builder.append(arrays[array_idx].value(row_idx)); + } + + let array = BooleanArray::new(values_builder.build(), interleaved.nulls); + Ok(Arc::new(array)) +} + fn interleave_bytes<T: ByteArrayType>( values: &[&dyn Array], indices: &[(usize, usize)], @@ -994,6 +1012,68 @@ mod tests { assert_eq!(&v, &[Some(2), None, None, Some(4), Some(3)]) } + #[test] + fn test_interleave_boolean() { + let a = BooleanArray::from(vec![Some(true), Some(false), None, Some(true)]); + let b = BooleanArray::from(vec![Some(false), Some(true), Some(true), None]); + + let values = interleave(&[&a, &b], &[(0, 0), (1, 1), (0, 2), (1, 3), (1, 2)]).unwrap(); + + let result = values.as_boolean(); + + assert_eq!( + result.iter().collect::<Vec<_>>(), + vec![Some(true), Some(true), None, None, Some(true),] + ); + } + + #[test] + fn test_interleave_boolean_sliced() { + let original = BooleanArray::from(vec![ + Some(false), + Some(true), + Some(false), + Some(true), + None, + Some(false), + Some(true), + ]); + + let sliced_array = original.slice(1, 5); + let sliced = sliced_array + .as_any() + .downcast_ref::<BooleanArray>() + .unwrap(); + let other = BooleanArray::from(vec![Some(false), Some(false), Some(true)]); + + let values = + interleave(&[sliced, &other], &[(0, 0), (1, 2), (0, 3), (0, 4), (1, 0)]).unwrap(); + + let result = values.as_boolean(); + + assert_eq!( + result.iter().collect::<Vec<_>>(), + vec![Some(true), Some(true), None, Some(false), Some(false),] + ); + } + + #[test] + fn test_interleave_boolean_fallback_matches_specialized() { + let a = BooleanArray::from_iter( + (0..1024).map(|i| if i % 10 == 0 { None } else { Some(i % 2 == 0) }), + ); + let b = BooleanArray::from_iter((0..1024).map(|i| Some(i % 3 == 0))); + + let indices: Vec<_> = (0..1024).map(|i| (i % 2, (i * 37) % 1024)).collect(); + + let values: [&dyn Array; 2] = [&a, &b]; + + let specialized = interleave_boolean(&values, &indices).unwrap(); + let fallback = interleave_fallback(&values, &indices).unwrap(); + + assert_eq!(specialized.as_ref(), fallback.as_ref()); + } + #[test] fn test_primitive_empty() { let a = Int32Array::from_iter_values([1, 2, 3, 4]);
