Repository navigation
Description
Is your feature request related to a problem or challenge?
While working on apache/datafusion#24426 (comment)
I am profiling ClickBench Q25 (the one query that is going slower for this setup)
SET datafusion.execution.parquet.binary_as_string = true;
CREATE EXTERNAL TABLE hits_raw STORED AS PARQUET LOCATION '/Users/andrewlamb/Software/datafusion/benchmarks/data/hits_partitioned/';
CREATE VIEW hits AS SELECT * EXCEPT ("EventDate"), CAST(CAST("EventDate" AS INTEGER) AS DATE) AS "EventDate" FROM hits_raw;
SET datafusion.execution.parquet.pushdown_filters = true;
SELECT "SearchPhrase" FROM hits WHERE "SearchPhrase" <> '' ORDER BY "SearchPhrase" LIMIT 10;About 5% of the overall time is going to reassembling the correct rows in the cached array reader
Describe the solution you'd like
Make it faster
Describe alternatives you've considered
It looks like the CachedArrayreader is currently using the classic "filter + concat" operation
arrow-rs/parquet/src/arrow/array_reader/cached_array_reader.rs
Lines 315 to 316 in 03b940e
| let filtered = arrow_select::filter::filter(&cached, &mask_array)?; | |
| selected_arrays.push(filtered); |
arrow-rs/parquet/src/arrow/array_reader/cached_array_reader.rs
Lines 335 to 336 in 03b940e
| 1 => Ok(selected_arrays.into_iter().next().unwrap()), | |
| _ => Ok(arrow_select::concat::concat( |
Which is what the coalesce kernel is designed to improve (avoids a copy)(: https://docs.rs/arrow/latest/arrow/compute/kernels/coalesce/index.html
So I think we could switch the CachedArrayReader to use coalesce to improve the performance
Additional context
No response
Activity
[-]Improve performance of[/-][+]Improve performance of CachedArrayreader filter+concat[/+]zhuqi-lucas commentedon Aug 25, 2026
cc @alamb — I can help with this if you haven't started, thanks!
alamb commentedon Aug 25, 2026
Thanks @zhuqi-lucas -- I have not started with it yet (I figured I would save my bandwidth to review it)
zhuqi-lucas commentedon Aug 28, 2026
Tried this and the coalesce approach doesn't help here: its single-pass path only fires for all-inline views. When views reference data buffers,
copy_rows_by_filter_fromdoes the same filter-then-remap two passes as today's code (the comment there even says "match the filter kernel").Measured where the time actually goes in
consume_batch(clickbench bench, Q12):filter~6%,concat~50% — and the concat cost is the per-viewbuffer_indexremap, not string data (str_len=20and128concat at the same speed; all-inline is 2.7x faster). I implemented three single-pass alternatives (interleave, index-based gather, run-based gather with the remap fused in) — all measurably slower: at ~80% selectivity nothing beats filter's run-based bulk copy.What did show up is a quadratic:
cleanup_consumed_batchesrescans batch ids from 0 and takes the cache write lock on everyconsume_batch. Fix in #10901 , but from the benchmark result, it seems not good performance improvement.The real opportunity the numbers point at: the single-slice path (filter only) shares the source buffers and does zero remapping — 0.058 ns/row vs 0.498 for the multi-slice path, which is only 12.8% of calls but covers 50% of all rows. Rather than a fused kernel, aligning the cache batch size/boundaries with
consume_batch's span (row_countreaches ~22k vsbatch_size=8192, hence the 7-slice concats) would make the zero-remap path the common case. Worth pursuing?alamb commentedon Sep 1, 2026
I don't fully understand this?
Adapt predicate cache batch granularity to make zero-rewrite reassemb…
Make CachedArrayReader cache cleanup incremental instead of quadratic (…
perf(parquet): fuse consecutive filters on the same projection (#10859)