This is an automated email from the ASF dual-hosted git repository.
alamb 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 3e6bd627c7 bench(parquet): cover mask-backed intersection/union in
row_selector (#10574)
3e6bd627c7 is described below
commit 3e6bd627c7b6d0dcda8aeb0a6efc829508177d4f
Author: Huaijin <[email protected]>
AuthorDate: Fri Aug 7 22:45:20 2026 +0800
bench(parquet): cover mask-backed intersection/union in row_selector
(#10574)
# Which issue does this PR close?
Split out of #10446 so that CI can compare both sides — the benchmark is
new there, so the merge-base has nothing to compare against and the
`main` column comes out empty.
# Rationale for this change
`row_selector.rs` already benchmarks `intersection`/`union`, but it
builds the operands with `from_filters`, which is selector-backed. Those
take the `RowSelector` merge path and never reach the bitwise one used
when both operands are mask-backed.
# What changes are included in this PR?
Adds `mask_intersection`/`mask_union`, varying the two dimensions that
drive the bitwise path:
- the ratio between operand lengths, since unequal lengths pass the
longer side's tail through unchanged
- the bit offsets the operands carry, since masks come from
`BooleanBuffer::slice` and whether the two share a sub-64-bit alignment
decides which path the underlying helpers take
Benchmark only, no library changes.
# Are these changes tested?
N/A — this is a benchmark. It builds and runs on `main` as-is.
# Are there any user-facing changes?
No.
---
parquet/benches/row_selector.rs | 56 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/parquet/benches/row_selector.rs b/parquet/benches/row_selector.rs
index cb18d55563..5d8abd3281 100644
--- a/parquet/benches/row_selector.rs
+++ b/parquet/benches/row_selector.rs
@@ -26,6 +26,27 @@ use std::hint;
/// [`RowSelector`]s per row, so the RLE encoding dominates.
const MASK_RUN_LENGTHS: &[usize] = &[1, 4, 16, 32, 48, 64, 96, 128];
+const MASK_ALGEBRA_ROWS: usize = 3_000_000;
+
+/// Operand length pairs. Unequal lengths pass the longer side's tail through
unchanged,
+/// so the ratio decides how much of the work is the bitwise combine versus
the tail.
+const MASK_ALGEBRA_LENGTHS: &[(&str, usize, usize)] = &[
+ ("equal", MASK_ALGEBRA_ROWS, MASK_ALGEBRA_ROWS),
+ ("tail1", MASK_ALGEBRA_ROWS, MASK_ALGEBRA_ROWS - 1),
+ ("tail1of3", MASK_ALGEBRA_ROWS, MASK_ALGEBRA_ROWS * 2 / 3),
+ ("tail_most", MASK_ALGEBRA_ROWS, 1_000),
+];
+
+/// Bit offsets for the left and right operand. Masks come from
[`BooleanBuffer::slice`],
+/// so a non-zero offset is normal. Whether the two share a sub-64-bit
alignment decides
+/// which path the underlying bitwise helpers take, so both are covered.
+const MASK_ALGEBRA_OFFSETS: &[(&str, usize, usize)] = &[
+ ("both_zero", 0, 0),
+ ("same_mod64", 3, 3),
+ ("same_mod64_far", 3, 67),
+ ("diff_mod64", 3, 5),
+];
+
/// Generates a random RowSelection with a specified selection ratio.
///
/// # Arguments
@@ -49,6 +70,40 @@ fn generate_run_length_mask(total_rows: usize, run_len:
usize) -> BooleanBuffer
BooleanBuffer::from_iter((0..total_rows).map(|row| (row /
run_len).is_multiple_of(2)))
}
+/// Builds a mask-backed [`RowSelection`] carrying `offset` as its bit offset.
+fn mask_algebra_operand(len: usize, offset: usize, selection_ratio: f64) ->
RowSelection {
+ let mut rng = rand::rng();
+ let bits: Vec<bool> = (0..len + offset)
+ .map(|_| rng.random_bool(selection_ratio))
+ .collect();
+ RowSelection::from_boolean_buffer(BooleanBuffer::from(bits).slice(offset,
len))
+}
+
+/// Benchmarks the bitwise `intersection`/`union` path, taken when both
operands are
+/// mask-backed. The `intersection`/`union` benchmarks above are
selector-backed and take
+/// the [`RowSelector`] merge path instead.
+fn bench_mask_backed_algebra(c: &mut Criterion, selection_ratio: f64) {
+ for (offset_label, left_offset, right_offset) in MASK_ALGEBRA_OFFSETS {
+ for (length_label, left_len, right_len) in MASK_ALGEBRA_LENGTHS {
+ let left = mask_algebra_operand(*left_len, *left_offset,
selection_ratio);
+ let right = mask_algebra_operand(*right_len, *right_offset,
selection_ratio);
+ let label = format!("{length_label}/{offset_label}");
+
+ c.bench_with_input(
+ BenchmarkId::new("mask_intersection", &label),
+ &(&left, &right),
+ |b, (left, right)| b.iter(||
hint::black_box(left.intersection(right))),
+ );
+
+ c.bench_with_input(
+ BenchmarkId::new("mask_union", &label),
+ &(&left, &right),
+ |b, (left, right)| b.iter(||
hint::black_box(left.union(right))),
+ );
+ }
+ }
+}
+
/// Benchmarks converting a mask-backed [`RowSelection`] into [`RowSelector`]s.
///
/// `RowSelection::iter` caches the RLE form, so a caller that iterates before
@@ -147,6 +202,7 @@ fn criterion_benchmark(c: &mut Criterion) {
})
});
+ bench_mask_backed_algebra(c, selection_ratio);
bench_mask_backed_conversion(c, total_rows, selection_ratio);
}