This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 0706d317a9 feat: specialize boolean interleave (#11501)
0706d317a9 is described below
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]);