bench(parquet): add async RowSelection policy baseline#10135
Conversation
|
Hi @alamb , could you take a look at this PR when you have a chance? It is a relatively small benchmark-only PR for Parquet row-filter/materialization policy coverage, with no production reader behavior changes. The current checks are green. |
Yes, sorry @hhhizzz -- I will do so The notion of what is a 'relatively small' PR has grown massively since even 9 months ago 😆 😭
|
alamb
left a comment
There was a problem hiding this comment.
Thank you @hhhizzz --- this is quite a comprehensive set of benchmarks -- however, I wonder if we really need all this coverage?
For example, there are 27 variants of FilterType -- I see there are some comments about some of the filter shapes matching particular TPCH / TPCDS predicate shapes, but I am trying ti figure out why a benchmark that compares all the different is going to be helpful in the long run to avoid regressions -- I worry taht this benchmark will generate so much data that we will find it hard to run / reason about
It seems like this benchmark may be most useful a development/tuning benchmark as it helps establish baseline timings for row-filter materialization policy choices: That is useful when designing future heuristics but it is hard to grok the results
Are there any important filter cases we should add to arrow_row_filter?
| /// FilterType encapsulates the different filter comparisons. | ||
| /// The variants correspond to the different filter patterns. | ||
| #[derive(Clone, Copy, Debug)] | ||
| pub(crate) enum FilterType { |
There was a problem hiding this comment.
this is similar to arrow_reader_row_filter -
arrow-rs/parquet/benches/arrow_reader_row_filter.rs
Lines 322 to 452 in cf450b5
Can we consolidate into a shared module like parquet/benches/row_filter_fixture.rs ?
There was a problem hiding this comment.
Done. I extracted the shared synthetic reader fixture into parquet/benches/arrow_reader_common/mod.rs and wired both arrow_reader_row_filter.rs and arrow_reader_materialization_policy.rs through it.
I used a bench-local module directory instead of a flat row_filter_fixture.rs so the shared helpers stay scoped to these reader benches. I can rename it if you prefer the flatter file name.
| ) | ||
| .await | ||
| } | ||
| AsyncStrategy::PushdownMask => { |
There was a problem hiding this comment.
the only thing different in each of these branches is the selection policy, right? We could probably collapse them significantly
There was a problem hiding this comment.
Done. The async strategy handling is now collapsed through AsyncStrategy::row_selection_policy(), so the pushdown path is shared and only the full post-filter path remains separate.
I took another pass at narrowing the benchmark scope. The materialization-policy target is now explicitly focused on policy-boundary/tuning cases rather than being a broad row-filter regression matrix:
For If this still feels too broad, I can trim the materialization-policy target further, but the current split is intended to keep generic row-filter behavior in |
|
@alamb Thanks for the feedback. I pushed an update that narrows this PR toward a smaller, maintainable benchmark baseline rather than a broad policy-tuning sweep. Main changes in this revision:
The intent is that I also measured the trimmed default Criterion runtime.(Tested a 24 core AMD64 linux machine)
For comparison, before the reduction these two targets took about Validation: cargo bench -p parquet --features arrow,async --no-run --bench arrow_reader_row_filter --bench arrow_reader_materialization_policyOne note: I left |
c0edcfc to
a7a20a6
Compare
a7a20a6 to
b52be0d
Compare
b52be0d to
eaec65a
Compare
|
@alamb I’ve force-pushed a substantially narrower, benchmark-only revision based on your feedback. The PR now contains 12 deterministic async |

Which issue does this PR close?
Rationale for this change
This PR lands a reusable benchmark baseline before changing Parquet reader
behavior. It measures the async push-decoder path with
RowSelectionPolicy::Auto, so the same benchmark IDs can be compared directlybetween
mainand candidate branches.The existing
row_selection_cursorbenchmark remains unchanged. It continuesto provide the low-level phase diagram for forced
MaskandSelectorswhen acaller already has a
RowSelection. Forced-policy and manualdecode-then-filter diagnostics are intentionally kept out of this daily Auto
baseline and can be added later as separate oracle benchmarks.
What changes are included in this PR?
This PR changes benchmarks only. Production reader code and public APIs are
unchanged.
It adds
arrow_reader_row_selection_policy, an Auto-only async benchmark backedby a deterministic in-memory Parquet fixture:
PageIndexPolicy::Skipmetadata loading.Page indexes are intentionally disabled so this target isolates RowSelection
execution and materialization from page-level I/O pruning. This is also why its
deterministic fixture remains separate from
arrow_reader_row_filter.The final 12 benchmark IDs are divided by the variable they isolate:
auto/shape— eight controlled selectivity and run-shape cases;auto/order— the same two sparse and two fragmented row groups in exactreverse order;
auto/scale— the fragmented shape at one and eight row groups.The shape cases are:
sparse_1_56_run32moderate_12_5_run32fragmented_50_run1regular_50_run32clustered_50_run128bursty_50_same_summarydense_98_44_skip1_select63all_selectedThe regular and bursty cases have identical selectivity, selector density,
mean selected run, and mean skipped run; only run variance and ordering differ.
The order cases have the same row-group multiset and output row count, so row
group order is their only variable.
Before Criterion starts timing, the preflight collects
payload_0from everyselected output row and compares it with the exact expected global-row
sequence. This detects wrong-row selection, output reordering, and row-group
boundary errors rather than checking only the total row count. The timed runner
still only consumes batches and accumulates rows.
No workload-derived cases are included yet. Future ClickBench or TPC-DS cases
should be based on captured selector traces with recorded provenance rather
than synthetic approximations.
Follow-up roadmap
mainversus candidate comparisons.Mask, forcedSelectors, andmanual decode-then-filter diagnostics when needed.
in a separate extended benchmark rather than the daily baseline.
regressions have been understood.
Are these changes tested?
Yes. Local validation included:
cargo fmt --all -- --check cargo clippy -p parquet --bench arrow_reader_row_selection_policy --features arrow,async -- -D warnings cargo bench -p parquet --bench arrow_reader_row_selection_policy --features arrow,async --no-run cargo bench -p parquet --bench arrow_reader_row_selection_policy --features arrow,async -- --list cargo bench -p parquet --bench arrow_reader_row_selection_policy --features arrow,async -- --test cargo test -p parquet --all-features git diff --checkAll 12 benchmark cases passed their exact correctness preflight locally.
A full default Criterion measurement was also run on AMD64 Linux
(
sz-data-b-1) ateaec65a202against basecd17899fe4:complete 100 samples for slower cases; no case failed;
fragmented_50_run1: 2.482 ms;regular_50_run32: 1.773 ms;bursty_50_same_summary: 1.801 ms;dense_98_44_skip1_select63: 1.950 ms.This run validates the target's runtime and observable shape signals; it is not
a current-versus-main performance comparison, and no performance improvement is
claimed in this PR.
Are there any user-facing changes?
No. This only changes benchmark code.
AI-assisted development
The benchmark design and implementation were developed with AI assistance and
reviewed against the requested benchmark scope, repository standards,
formatting, Clippy, the full Parquet test suite, release benchmark builds, and
the full Criterion run above.