Repository navigation
Conversation
|
|
There was a problem hiding this comment.
Comment update needed?
Perhaps even benchmark the change of behavior since new benchmark does not cover it?
There was a problem hiding this comment.
Thanks for pointing this out! I updated the comment and adjusted the benchmark to cover the new incremental batching behavior.
cefb414 to
66df1a0
Compare
66df1a0 to
131b2ee
Compare
| ASSERT_FINISHES_OK_AND_ASSIGN(auto first_batch, batch_generator()); | ||
| ASSERT_NE(first_batch, nullptr); | ||
| ASSERT_EQ(first_batch->num_columns(), num_columns); | ||
| ASSERT_EQ(first_batch->num_rows(), batch_size); |
There was a problem hiding this comment.
This test prints OK, then the process segfaults in ProxyMemoryPool::Free. The generator has already started another batch, but the test ends and destroys both memory pools.
Please wait for the background work before leaving this scope. Draining and discarding the remaining batches fixed my local run. Please check the benchmark's reader_pool lifetime too.
There was a problem hiding this comment.
Thanks for catching this! I’ve drained the generator and verified the benchmark’s pool lifetime.
Rationale for this change
The asynchronous Parquet record batch generator decoded an entire row group into memory before slicing it into record batches. For files containing large row groups, this caused high peak memory usage even when a smaller
batch_sizewas configured.What changes are included in this PR?
Are these changes tested?
Yes. Tests verify that:
Are there any user-facing changes?
There are no public API changes.
For pre-buffered Parquet dataset scans, the existing batch_size option now also limits the number of rows decoded per batch instead of only controlling how an already-decoded row group is sliced. This reduces peak memory usage for large row groups.
Was AI used for this PR?
AI was used to help trace the relevant call chain, review concurrency behavior, suggest tests, and draft portions of the implementation and PR description. The changes were reviewed before submission.
Benchmark
Workload: one 262,144-row group with 128
int64columns and pre-buffering enabled. Results are medians of five runs using Google Benchmark wall-clock time.The existing
use_threadsoption remains supported. At a 32,768-row batch size, threaded column decoding improved incremental throughput from 61.1M to 70.3M rows/s in this workload.These results show that incremental decoding turns the existing
batch_sizesetting into a direct memory-throughput tuning knob. Smaller batches reduce peak decoded memory, while larger batches recover throughput.With the eager path,
batch_sizecontrolled only the emitted batches; the complete row group was still decoded first, so peak decoded memory remained determined by row-group size. The incremental path lets users choose anappropriate point in this tradeoff instead of always accepting a row-group-sized decoded allocation.
A 1,481,181-row, 650-column Parquet file with one row group was scanned in a fresh process for each case.
The incremental path reduced Arrow peak memory by 89.4% at a 65,536-row batch size and by 79.0% at 131,072 rows. The eager path's peak was unchanged across batch sizes, while incremental memory scaled with the configured batch size.
Associated Arrow Issue Links
#36778
#20473
Downstream relevance
We encountered this behavior while investigating high memory usage in PyIceberg’s
to_arrow_batch_reader(). Tracing the call path into PyArrow showed that the asynchronous Parquet scanner decoded an entire row group before applying the configured batch_size. This Arrow-level behavior can amplify memory usage when Parquet files contain large row groups. The change in this PR makes decoding incremental for the pre-buffered scanner path. This will also help address concerns similar to ours for decoding large row groups using PyIceberg’sto_arrow_batch_readerPR code and description written by:
Reviewed before submission by: