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-11714-fb88f81d39601a5d0244d8ef034d7e2a10788c60 in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 08928b340c77062c1c343815e2d140f090bd110b Author: Michael Hoffmann <[email protected]> AuthorDate: Sun Oct 25 22:59:04 2026 +0000 bench(arrow): cover interleave of lists with primitive struct children (#11714) # Rationale for this change List interleave can copy primitive child ranges directly, but a Struct child takes the expanded-index path. Add coverage before changing that implementation so its costs and existing primitive-list behavior can be compared independently. # What changes are included in this PR? Extend interleave_kernels with 4,096 alternating parent selections from six inputs. Cover Int64/Float64 Struct children with singleton, 15- and 90-element, and mixed-length lists, including independently nullable and sliced arrays. Include matching primitive-list controls. # Are these changes tested? The new benchmark cases build and run against the unmodified kernel and the proposed optimization using the actual Arrow workspace. No library code changes are included in this commit. # Are there any significant user-facing changes? No. # AI usage AI assistance was used to write and review the benchmark and run the comparisons. Human review and ownership remain with the submitter. Co-authored-by: RIchard Baah <[email protected]> --- arrow/benches/interleave_kernels.rs | 77 ++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/arrow/benches/interleave_kernels.rs b/arrow/benches/interleave_kernels.rs index 3ccc82fa1f..35cccf215a 100644 --- a/arrow/benches/interleave_kernels.rs +++ b/arrow/benches/interleave_kernels.rs @@ -26,7 +26,7 @@ use rand::RngExt; use arrow::datatypes::*; use arrow::util::test_util::seedable_rng; use arrow::{array::*, util::bench_util::*}; -use arrow_buffer::ScalarBuffer; +use arrow_buffer::{NullBuffer, OffsetBuffer, ScalarBuffer}; use arrow_select::interleave::interleave; use std::hint; use std::sync::Arc; @@ -222,5 +222,78 @@ fn add_benchmark(c: &mut Criterion) { } } -criterion_group!(benches, add_benchmark); +// Alternate sources so parent rows cannot coalesce, while each selected list +// contributes a contiguous range of 15 children. Compare struct children with +// the existing primitive-copy path, with and without nulls. +// Both variants use sliced inputs. +fn bench_list_ranges(c: &mut Criterion) { + const ROWS: usize = 4096; + const SOURCES: usize = 6; + let indices: Vec<_> = (0..ROWS).map(|i| (i % SOURCES, i / SOURCES)).collect(); + for nullable in [false, true] { + for primitive in [false, true] { + let arrays: Vec<ArrayRef> = (0..SOURCES) + .map(|source| { + let rows = ROWS.div_ceil(SOURCES) + 3; + let offsets = OffsetBuffer::<i32>::from_lengths(std::iter::repeat_n(15, rows)); + let len = *offsets.as_ref().last().unwrap() as usize; + let nulls = |len, period| { + (nullable && source % 3 != 0).then(|| { + NullBuffer::from_iter((0..len).map(|i| (i + source) % period != 0)) + }) + }; + let ints = Int64Array::new( + (0..len + 6) + .map(|i| (i + source) as i64) + .collect::<Vec<_>>() + .into(), + nulls(len + 6, 5), + ) + .slice(1, len + 5); + let values: ArrayRef = if primitive { + Arc::new(ints.slice(5, len)) + } else { + let floats = Float64Array::new( + (0..len + 8) + .map(|i| (i + source) as f64 * 0.25) + .collect::<Vec<_>>() + .into(), + nulls(len + 8, 3), + ) + .slice(3, len + 5); + Arc::new( + StructArray::new( + vec![ + Field::new("a", DataType::Int64, true), + Field::new("b", DataType::Float64, true), + ] + .into(), + vec![Arc::new(ints), Arc::new(floats)], + nulls(len + 5, 7), + ) + .slice(5, len), + ) + }; + Arc::new( + ListArray::new( + Arc::new(Field::new_list_field(values.data_type().clone(), true)), + offsets, + values, + nulls(rows, 11), + ) + .slice(3, rows - 3), + ) as ArrayRef + }) + .collect(); + let values: Vec<_> = arrays.iter().map(|a| a.as_ref()).collect(); + let child = if primitive { "primitive" } else { "struct" }; + let validity = if nullable { "nulls" } else { "no_nulls" }; + c.bench_function(&format!("list_ranges/{child}/15/{validity}"), |b| { + b.iter(|| hint::black_box(interleave(&values, &indices).unwrap())) + }); + } + } +} + +criterion_group!(benches, add_benchmark, bench_list_ranges); criterion_main!(benches);
