hhhizzz opened a new pull request, #9956:
URL: https://github.com/apache/arrow-rs/pull/9956

   # Which issue does this PR close?
   
   - Closes #8846
   - Part of #7456
   - Addresses #9591
   - Related to #9589
   - Follow-up to #8565
   
   # Rationale for this change
   
   `RowFilter` can be much slower than a full scan for fragmented selections, 
especially when page indexes provide little pruning and the resulting 
`ReadPlan` contains many tiny select/skip runs. #8565 shows an extreme case 
where predicate pushdown is around 10x slower than scanning and filtering 
afterwards.
   
   This PR improves the `RowSelectionPolicy::Auto` path so it can make better 
strategy decisions for fragmented row selections and avoid continuing with 
row-filter pushdown when the observed shape suggests the pushdown path is 
unlikely to help.
   
   The main goal is to reduce the performance cliff without changing explicit 
`Mask` / `Selectors` behavior.
   
   While working on the Auto strategy, this PR also fixes a correctness issue 
in the `Mask` execution path. With sparse page-loaded ranges, a mask-backed 
read plan could previously attempt to consume selected rows outside the loaded 
ranges and fail during decoding. This made `Mask` risky for some page-index / 
fragmented-selection cases.
   
   This PR makes the loaded row ranges explicit in the read plan and adds 
coverage for sparse loaded-range execution. After this change, using `Mask` 
should no longer hit this known failure mode.
   
   
   # What changes are included in this PR?
   
   - Adds structured `RowSelection` shape analysis and strategy decision 
metrics.
   - Improves `RowSelectionPolicy::Auto` so it can choose between mask and 
selectors using selection shape and loaded page ranges.
   - Adds sparse loaded-range tracking from page offsets so fragmented 
page-loaded selections can avoid expensive mask execution.
   - Adds adaptive post-filter fallback for Auto:
     - observes the first row group;
     - keeps pushdown for sparse/low-selectivity cases where it is still useful;
     - switches later row groups to post-filter for high-selectivity or 
fragmented moderate/high-selectivity cases.
   - Preserves correctness around fallback:
     - does not reuse sparse predicate chunks when rebuilding a base/full read;
     - disables post-filter fallback for `try_next_reader` handoff paths;
     - avoids evaluating `ArrowPredicate` twice for the same current row group 
when caller-provided row selection is present.
   - Simplifies fallback trigger metrics to shape-based reasons only, avoiding 
unused timing/cache fields.
   - Replaces loaded range intersection with a linear two-pointer merge.
   - Extends `arrow_reader_row_filter` benchmarks to cover strategy-sensitive 
cases.
   - Fixes a known `Mask` correctness failure with sparse loaded page ranges.
   - Preserves explicit `Mask` behavior while ensuring sparse loaded ranges are 
tracked so mask execution does not read outside available page ranges.
   
   
   # Are these changes tested?
   
   Yes.
   
   Unit / integration validation:
   
   - `cargo fmt -p parquet -- --check`
   - `git diff --check`
   - `cargo test -p parquet --lib arrow::push_decoder`
     - 38 passed
   - `cargo test -p parquet --lib arrow::arrow_reader::read_plan`
     - 28 passed
   - `cargo test -p parquet --lib arrow::arrow_reader::selection`
     - 25 passed
   - `cargo test -p parquet --lib`
     - 1132 passed
   
   New focused tests cover:
   
   - Auto fallback with caller-provided row selection does not evaluate the 
current row group predicate twice.
   - `try_next_reader` does not use post-filter fallback in normal reader 
handoff mode.
   - Sparse/current-row fallback does not reuse incompatible predicate chunks.
   - Explicit `Mask` execution remains correct with sparse loaded page ranges.
   - Auto switches away from `Mask` when sparse loaded ranges make selector 
execution safer.
   - Multi-column loaded row-range intersection remains correct after switching 
to a linear merge.
   - Auto strategy decisions for sparse loaded ranges and expensive fragmented 
output.
   
   Benchmark evidence from `arrow_reader_row_filter` comparing `origin/main` vs 
this branch:
   
   | case | origin/main | this branch | change |
   |---|---:|---:|---:|
   | `utf8View != ''`, all columns | 7.91 ms | 5.52 ms | +43.2% |
   | `int64 > 90`, all columns | 7.29 ms | 5.34 ms | +36.5% |
   | `int64 > 90`, exclude filter column | 6.97 ms | 5.23 ms | +33.3% |
   | `utf8View != ''`, exclude filter column | 6.77 ms | 5.53 ms | +22.5% |
   | `float64 > 99.0`, exclude filter column | 4.94 ms | 4.38 ms | +12.9% |
   
   Across the 16 async `arrow_reader_row_filter` cases run, geometric mean 
speedup was about 1.08x. The worst observed regression in that run was about 
-3.3%.
   
   
   # Are there any user-facing changes?
   
   No intended breaking API changes.
   
   `RowSelectionPolicy::Auto` may choose different internal execution 
strategies than before. Explicit `Mask` and `Selectors` policies remain 
available for callers that want fixed behavior.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to