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-11768-b90914fecd0ba85a8827f386af3bcc51cce5b162 in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 3db18f75b40921b8abf17daed32999ec58064f7d Author: Neil Conway <[email protected]> AuthorDate: Fri Oct 23 05:42:00 2026 +0000 bench: improve `concat`, `coalesce_kernels` benchmarks (#11768) # Which issue does this PR close? - N/A # Rationale for this change This PR improves the `concat` and `coalesce_kernels` benchmarks: * `concat`: Add a case that mixes inline and out-of-line strings (3 to 19 bytes). Previously, every string in a case had the same length (12, 20 or 128 bytes). * `concat`: Each case concatenated 10 copies of the same 8192-row array, so after the first copy the input views were already in cache. Real-world data doesn't look like this; each each iteration now concatenates the next 10 of 32 arrays with different seeds. * `coalesce_kernels`: Add a benchmark case for out-of-line strings that don't take the copy-path in the coalescer. * `coalesce_kernels`: For `Utf8View`, `Utf8`, and `Dictionary(Int32, Utf8)` inputs, the RNG seed was ignored. Fix it to pass the seed to the RNG, so we don't generate identical data. # What changes are included in this PR? See above. # Are these changes tested? Benchmark change only. # Are there any significant user-facing changes? No, benchmark change only. --- arrow/benches/coalesce_kernels.rs | 65 ++++++++++++++++++++++++++++++++----- arrow/benches/concatenate_kernel.rs | 44 +++++++++++++++++++------ 2 files changed, 90 insertions(+), 19 deletions(-) diff --git a/arrow/benches/coalesce_kernels.rs b/arrow/benches/coalesce_kernels.rs index d43001a514..9cdd71cdc3 100644 --- a/arrow/benches/coalesce_kernels.rs +++ b/arrow/benches/coalesce_kernels.rs @@ -169,6 +169,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &primitive_schema, } @@ -181,6 +182,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &single_schema, } @@ -193,11 +195,26 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 8, schema: &single_schema, } .build(); + // Model strings that are all stored out of line, such as UUIDs + FilterBenchmarkBuilder { + c, + name: "single_utf8view (string_len=13..40)", + batch_size, + num_output_batches: 50, + null_density, + selectivity, + min_string_len: 13, + max_string_len: 40, + schema: &single_schema, + } + .build(); + FilterBenchmarkBuilder { c, name: "single_binaryview", @@ -205,6 +222,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &single_binaryview_schema, } @@ -217,6 +235,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 8, schema: &single_binaryview_schema, } @@ -229,6 +248,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &single_utf8_schema, } @@ -241,6 +261,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &single_binary_schema, } @@ -254,6 +275,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 20, schema: &mixed_utf8view_schema, } @@ -266,6 +288,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 8, schema: &mixed_utf8view_schema, } @@ -279,6 +302,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 128, schema: &mixed_utf8view_schema, } @@ -291,6 +315,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 20, schema: &mixed_binaryview_schema, } @@ -303,6 +328,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 8, schema: &mixed_binaryview_schema, } @@ -315,6 +341,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 128, schema: &mixed_binaryview_schema, } @@ -327,6 +354,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 20, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &mixed_utf8_schema, } @@ -339,6 +367,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 10, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &mixed_dict_schema, } @@ -351,6 +380,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 25, null_density, selectivity, + min_string_len: 0, max_string_len: 30, schema: &single_dictionary_schema, } @@ -363,6 +393,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 16, schema: &single_fsb16_schema, } @@ -375,6 +406,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 32, schema: &single_fsb32_schema, } @@ -387,6 +419,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 0, schema: &single_boolean_schema, } @@ -399,6 +432,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 25, null_density, selectivity, + min_string_len: 0, max_string_len: 0, schema: &single_run_end_schema, } @@ -411,6 +445,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 0, schema: &single_fsl4_schema, } @@ -423,6 +458,7 @@ fn add_all_filter_benchmarks(c: &mut Criterion) { num_output_batches: 50, null_density, selectivity, + min_string_len: 0, max_string_len: 0, schema: &single_list_schema, } @@ -743,6 +779,8 @@ struct FilterBenchmarkBuilder<'a> { null_density: f32, /// between 0.0 .. 1.0, percent of rows that should be kept by the filter selectivity: f32, + /// The minimum length of strings in the data stream + min_string_len: usize, /// The maximum length of strings in the data stream /// /// For StringViewArray, strings <= 12 bytes are stored inline, longer @@ -762,6 +800,7 @@ impl FilterBenchmarkBuilder<'_> { num_output_batches, null_density, selectivity, + min_string_len, max_string_len, schema, } = self; @@ -775,6 +814,7 @@ impl FilterBenchmarkBuilder<'_> { let data = DataStreamBuilder::new(Arc::clone(schema)) .with_batch_size(batch_size) .with_null_density(null_density) + .with_min_string_len(min_string_len) .with_max_string_len(max_string_len) .build(); @@ -1317,6 +1357,7 @@ struct DataStreamBuilder { batch_size: usize, null_density: f32, num_batches: usize, // number of unique batches to create + min_string_len: usize, // minimum length of strings in the data stream max_string_len: usize, // maximum length of strings in the data stream } @@ -1327,6 +1368,7 @@ impl DataStreamBuilder { batch_size: 8192, null_density: 0.0, num_batches: 10, + min_string_len: 0, max_string_len: 30, } } @@ -1344,6 +1386,11 @@ impl DataStreamBuilder { self } + fn with_min_string_len(mut self, min_string_len: usize) -> Self { + self.min_string_len = min_string_len; + self + } + fn with_max_string_len(mut self, max_string_len: usize) -> Self { self.max_string_len = max_string_len; self @@ -1368,6 +1415,7 @@ impl DataStreamBuilder { batch_size, null_density: _, num_batches: _, + min_string_len: _, max_string_len: _, } = self; @@ -1408,23 +1456,22 @@ impl DataStreamBuilder { >( self.batch_size, self.null_density, - 0, + self.min_string_len, self.max_string_len, b"", seed, )), - DataType::Utf8View => { - Arc::new(create_string_view_array_with_max_len( - self.batch_size, - self.null_density, - self.max_string_len, - )) // TODO seed - } + DataType::Utf8View => Arc::new(create_string_view_array_with_len_range_and_seed( + self.batch_size, + self.null_density, + self.min_string_len..self.max_string_len, + seed, + )), DataType::BinaryView => Arc::new(BinaryViewArray::from_iter( create_binary_array_with_len_range_and_prefix_and_seed::<i32>( self.batch_size, self.null_density, - 0, + self.min_string_len, self.max_string_len, b"", seed, diff --git a/arrow/benches/concatenate_kernel.rs b/arrow/benches/concatenate_kernel.rs index 7e4f90f354..fbc2e87c72 100644 --- a/arrow/benches/concatenate_kernel.rs +++ b/arrow/benches/concatenate_kernel.rs @@ -144,17 +144,41 @@ fn add_benchmark(c: &mut Criterion) { }); } - // String view arrays + // String view arrays. Each iteration concatenates the next 10 of + // `NUM_VIEW_INPUTS` arrays, so the CPU can't learn the order of inline and + // out-of-line strings, or of nulls, in the input. + const NUM_VIEW_INPUTS: usize = 32; for null_density in [0.0, 0.2] { - // Any strings less than 12 characters are stored as prefix only, so specially - // benchmark cases that have different mixes of lengths. - for (name, str_len) in [("all_inline", 12), ("", 20), ("", 128)] { - let array = create_string_view_array_with_len(8192, null_density, str_len, false); - let arrays = (0..10).map(|_| &array as &dyn Array).collect::<Vec<_>>(); - let id = format!( - "concat utf8_view {name} max_str_len={str_len} null_density={null_density}" - ); - c.bench_function(&id, |b| b.iter(|| bench_concat_arrays(&arrays))); + // Strings of 12 bytes or fewer are stored inline in the view, so the + // cases cover all inline, all out of line, and a mix of the two. + for (name, lengths) in [ + ("all_inline str_len=12", 12..13), + ("str_len=20", 20..21), + ("str_len=128", 128..129), + ("mixed str_len=3..20", 3..20), + ] { + let inputs = (0..NUM_VIEW_INPUTS as u64) + .map(|seed| { + create_string_view_array_with_len_range_and_seed( + 8192, + null_density, + lengths.clone(), + seed, + ) + }) + .collect::<Vec<_>>(); + let mut next = inputs.iter().cycle(); + let id = format!("concat utf8_view {name} null_density={null_density}"); + c.bench_function(&id, |b| { + b.iter(|| { + let arrays = next + .by_ref() + .take(10) + .map(|a| a as &dyn Array) + .collect::<Vec<_>>(); + bench_concat_arrays(&arrays) + }) + }); } }
