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-11412-8bdf349ae69196d757cd0f6e3df4b9651fbc445d in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 2b7cab8551afa450f8178c1bec2427399bf03668 Author: RIchard Baah <[email protected]> AuthorDate: Wed Oct 7 01:37:32 2026 +0000 bench(arrow-cmp): add dict_cmp benchmark (#11412) # Which issue does this PR close? N/A — split out of https://github.com/apache/arrow-rs/pull/11407 # Rationale for this change `arrow-cmp` has no benchmark coverage for its dictionary comparator path, so the cost of `make_comparator` on `DictionaryArray`s is not visible in CI or local perf runs. # What changes are included in this PR? - `arrow-cmp/benches/dict_cmp.rs`; new criterion benchmark. Builds `Dictionary<Int32, Utf8>` arrays with 1M rows at each of 8 cardinalities (`10, 50, 100, 500, 750, 1000, 4000, 8192`), seeded for reproducibility. Each iteration constructs the comparator via `make_comparator` and invokes the returned closure 100 times on pseudo-random index pairs. # Are there any user-facing changes? no --- Cargo.lock | 2 ++ arrow-cmp/Cargo.toml | 6 ++++ arrow-cmp/benches/dict_cmp.rs | 80 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 07843eb699..a97ccd4ea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,7 +314,9 @@ dependencies = [ "arrow-array", "arrow-buffer", "arrow-schema", + "criterion", "half", + "rand 0.10.3", ] [[package]] diff --git a/arrow-cmp/Cargo.toml b/arrow-cmp/Cargo.toml index 9109f2b9a1..c4b00f32c7 100644 --- a/arrow-cmp/Cargo.toml +++ b/arrow-cmp/Cargo.toml @@ -41,6 +41,12 @@ arrow-schema = { workspace = true } [dev-dependencies] half = { version = "2.1", default-features = false, features = ["num-traits"] } +criterion = { workspace = true } +rand = { version = "0.10", default-features = false, features = ["std", "std_rng", "thread_rng"] } + +[[bench]] +name = "dict_cmp" +harness = false [lints] workspace = true diff --git a/arrow-cmp/benches/dict_cmp.rs b/arrow-cmp/benches/dict_cmp.rs new file mode 100644 index 0000000000..52d1f809b8 --- /dev/null +++ b/arrow-cmp/benches/dict_cmp.rs @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::hint::black_box; +use std::sync::Arc; + +use arrow_array::types::Int32Type; +use arrow_array::{DictionaryArray, Int32Array, StringArray}; +use arrow_cmp::make_comparator; +use arrow_schema::SortOptions; +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use rand::rngs::StdRng; +use rand::{RngExt, SeedableRng}; + +const BATCH_SIZE: usize = 8_192; +const CALLS: usize = 100; +const CARDINALITIES: [usize; 8] = [10, 50, 100, 500, 750, 1_000, 4_000, 8_192]; +const SEED: u64 = 0xC0FFEE; + +fn build_dict(rows: usize, cardinality: usize, seed: u64) -> DictionaryArray<Int32Type> { + let values: Vec<String> = (0..cardinality).map(|i| format!("v{i:07}")).collect(); + let values = StringArray::from(values); + + let mut rng = StdRng::seed_from_u64(seed); + let keys: Int32Array = (0..rows) + .map(|_| rng.random_range(0..cardinality) as i32) + .collect(); + + DictionaryArray::<Int32Type>::try_new(keys, Arc::new(values)).unwrap() +} + +fn index_pairs(bound: usize, count: usize) -> Vec<(usize, usize)> { + let mut rng = StdRng::seed_from_u64(SEED.wrapping_add(0x1234)); + (0..count) + .map(|_| (rng.random_range(0..bound), rng.random_range(0..bound))) + .collect() +} + +fn bench_dict_cmp(c: &mut Criterion) { + let pairs = index_pairs(BATCH_SIZE, CALLS); + + let mut group = c.benchmark_group("dict_cmp_utf8/100_calls_8k_batch"); + group.throughput(Throughput::Elements(CALLS as u64)); + group.sample_size(10); + + for &card in &CARDINALITIES { + let left = build_dict(BATCH_SIZE, card, SEED); + let right = build_dict(BATCH_SIZE, card, SEED ^ 0xA5A5_A5A5); + + group.bench_with_input(BenchmarkId::from_parameter(card), &card, |b, _| { + b.iter(|| { + let cmp = make_comparator(&left, &right, SortOptions::default()).unwrap(); + let mut acc = 0i64; + for &(i, j) in &pairs { + acc += cmp(i, j) as i64; + } + black_box(acc) + }) + }); + } + + group.finish(); +} + +criterion_group!(benches, bench_dict_cmp); +criterion_main!(benches);
