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);
 }
 

Reply via email to