[Parquet] Vectorise Pack Bits within BitWriter - #10432
Conversation
|
run benchmark arrow_writer |
This comment was marked as duplicate.
This comment was marked as duplicate.
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: CPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
run benchmark arrow_writer |
This comment was marked as duplicate.
This comment was marked as duplicate.
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: CPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
@Jefffrey @etseidl could you please take a look at this? I ran the same bench on my machine and don't get the regressions from the previous bot run. ( My last run if from a different main. My machine is x86) |
|
lets split the bench changes to a separate PR so the bot can compare them from main |
|
run benchmark bit_packing |
|
🤖 Arrow criterion benchmark running (GKE) | trigger CPU Details (lscpu)Comparing put_batch (3fef6c0) to 901e69f (merge-base) diff Run configurationrun benchmark bit_packingBENCH_COMMAND=cargo bench --features=arrow,async,test_common,experimental,object_store --bench bit_packing File an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: Comparing put_batch (3fef6c0) to 901e69f (merge-base) diff Run configurationrun benchmark bit_packingCPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
i do still have this in my review backlog, hopefully will get to it eventually 😅 |
|
@alamb this is ALP related 👀 |
# Conflicts: # parquet/src/encodings/rle.rs
|
I plan to review this -- I have been out at VLDB this week so my review backlog is quite large now |
|
run benchmark bit_packing alp |
|
🤖 Arrow criterion benchmark running (GKE) | trigger CPU Details (lscpu)Comparing put_batch (3f8e0de) to cb682a2 (merge-base) diff Run configurationrun benchmark bit_packingBENCH_COMMAND=cargo bench --features=arrow,async,test_common,experimental,object_store --bench bit_packing File an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark running (GKE) | trigger CPU Details (lscpu)Comparing put_batch (3f8e0de) to cb682a2 (merge-base) diff Run configurationrun benchmark alpBENCH_COMMAND=cargo bench --features=arrow,async,test_common,experimental,object_store --bench alp File an issue against this benchmark runner |
|
Benchmark for this request failed before finishing (Kubernetes reason: Benchmarks requested: Runner log (last 40 lines)Kubernetes messageFile an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: Comparing put_batch (3f8e0de) to cb682a2 (merge-base) diff Run configurationrun benchmark bit_packingCPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
Want to test some things before we proceed |
|
run benchmark bit_packing alp |
|
🤖 Arrow criterion benchmark running (GKE) | trigger CPU Details (lscpu)Comparing put_batch (77cf89a) to b06b9c1 (merge-base) diff Run configurationrun benchmark bit_packingBENCH_COMMAND=cargo bench --features=arrow,async,test_common,experimental,object_store --bench bit_packing File an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark running (GKE) | trigger CPU Details (lscpu)Comparing put_batch (77cf89a) to b06b9c1 (merge-base) diff Run configurationrun benchmark alpBENCH_COMMAND=cargo bench --features=arrow,async,test_common,experimental,object_store --bench alp File an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: Comparing put_batch (77cf89a) to b06b9c1 (merge-base) diff Run configurationrun benchmark bit_packingCPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
|
🤖 Arrow criterion benchmark completed (GKE) | trigger Instance: Comparing put_batch (77cf89a) to b06b9c1 (merge-base) diff Run configurationrun benchmark alpCPU Details (lscpu)Details
Resource Usagebase (merge-base)
branch
File an issue against this benchmark runner |
| // filled trailing group. Only materialized into `bit_writer` when the run | ||
| // closes, so the whole run can be packed in one vectorised `put_batch` call. | ||
| // Bounded by `MAX_GROUPS_PER_BIT_PACKED_RUN * BIT_PACK_GROUP_SIZE` values. | ||
| pending_values: Vec<u64>, |
There was a problem hiding this comment.
I think the bound is always 512 values or less (so this would be at most a 4KB buffer)
Did you consider just leaving as a fixed size buffer to avoid an allocation / indirection?
Something like
buffered_values: [u64; MAX_GROUPS_PER_BIT_PACKED_RUN*BIT_PACK_GROUP_SIZE],🤔
sunchao
left a comment
There was a problem hiding this comment.
Verdict: Approve With Non-Blocking Follow-Ups
I re-reviewed current head 292cd344, including all changed files and their consumers. No confirmed correctness bug or blocking design/performance issue found.
Correctness
- Packing preserves low-bit masking, signed integer bit patterns, byte ordering, alignment, and trailing values.
- RLE batching preserves run transitions, headers, padding, and mixed scalar/batch behavior.
- The ALP and delta changes safely reuse buffers after their original contents are no longer needed. I found no overflow or cross-page reuse issue.
- This PR changes Parquet encoding, not Spark expressions/operators. No Spark SQL semantic changes were identified.
Performance
The batching removes repeated per-value work, and the earlier focused benchmarks showed substantial improvements. Reusing existing ALP/delta buffers also avoids additional scratch allocations.
The concrete tradeoff is the new RLE staging vector: allocation/growth overhead and roughly 4 KiB retained capacity per populated encoder. Comparing it with a fixed buffer is reasonable, as an existing reviewer suggested, but a fixed buffer also enlarges every encoder, including small workloads.
The earlier list/Bloom slowdown remains inconclusive, not a demonstrated PR defect.
Design
The layering is sensible: shared packing kernels, a batch-capable BitWriter, and small caller changes. Deferring a complete RLE literal run enables batching and removes header-backpatch bookkeeping. The additional state-machine complexity is justified, though worth protecting with strong tests.
Abstraction
BitPacking naturally groups packing and unpacking. Separate single-block and multi-block entry points avoid repeated width dispatch. I found no unnecessary abstraction or compelling reason for a broader redesign. The trait rename affects the explicitly experimental API, not the normal stable API.
Non-Blocking Follow-Ups
- Retain the performance concern as a follow-up, including the buffer-allocation comparison.
- Strengthen the RLE parity test with deterministic empty batches, widths 0/64, and flush/clear/reuse cases. Our independent probes passed these cases, but they are not all preserved in the PR’s tests.
Validation
Freshly ran 355 focused crate tests: all passed, plus independent packing and RLE boundary probes. Current CI includes passing PySpark integration, MIRI, and MSRV checks. CI results
Local tests used the previously disclosed crc32fast dependency adjustment. I did not run the entire workspace suite or post a GitHub review.
# Which issue does this PR close? <!-- We generally require a GitHub issue to be filed for all bug fixes and enhancements and this helps us generate change logs for our releases. You can link an issue to this PR using the GitHub syntax. --> - Closes apache#2257. # Rationale for this change Perf 🚀 <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. --> # What changes are included in this PR? - Renamed the `FromBitpacked` to `BitPacking` to store the Packing APIs - Added vectorised `pack_batch` matching `unpack_batch` kernels - Added unit tests - Added bench arms <!-- There is no need to duplicate the description in the issue here but it is sometimes worth providing a summary of the individual changes in this PR. --> # Are these changes tested? Yes! <!-- We typically require tests for all PRs in order to: 1. Prevent the code from being accidentally broken by subsequent changes 2. Serve as another way to document the expected behavior of the code If tests are not included in your PR, please explain why (for example, are they covered by existing tests)? If this PR claims a performance improvement, please include evidence such as benchmark results. --> # Are there any user-facing changes? No <!-- If there are user-facing changes then we may require documentation to be updated before approving the PR. If there are any breaking changes to public APIs, please call them out. --> --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
Which issue does this PR close?
Rationale for this change
Perf 🚀
What changes are included in this PR?
FromBitpackedtoBitPackingto store the Packing APIspack_batchmatchingunpack_batchkernelsAre these changes tested?
Yes!
Are there any user-facing changes?
No