From 74f87426532cb08b1e6e4252ca80d15351c27c0a Mon Sep 17 00:00:00 2001 From: Prateek Gaur Date: Sat, 5 Sep 2026 19:44:29 +0000 Subject: [PATCH] [C++][Parquet] Avoid reloading DELTA_BINARY_PACKED decoder state for every value min_delta_ and last_value_ have the same type as GetInternal's output buffer, and that buffer points into memory the caller owns, so the compiler cannot prove the prefix-sum store does not land on either member: it reloads the frame and stores the running value on every value. On aarch64 with GCC 11.5 the loop body is 8 instructions with 4 memory operations per value, where 6 and 2 are enough. Hold the running value and the frame in locals across the loop and write last_value_ back once when it ends. The arithmetic is unchanged - every term stays in the unsigned type, so the wrapping the existing comment documents is preserved and decoded values are identical. On the DELTA_BINARY_PACKED decode benchmarks already in the tree this is 1.26x to 1.28x wherever the running sum is a meaningful share of the work. --- cpp/src/parquet/decoder.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/src/parquet/decoder.cc b/cpp/src/parquet/decoder.cc index c4d3fe5a8a5a..9a27c575f226 100644 --- a/cpp/src/parquet/decoder.cc +++ b/cpp/src/parquet/decoder.cc @@ -1658,13 +1658,19 @@ class DeltaBitPackDecoder : public TypedDecoderImpl { values_decode) { ParquetException::EofException(); } + // Hold the running value and the frame in locals. Both are members of the + // same type as the output, so a store to `buffer` may alias them and the + // compiler must otherwise reload min_delta_ and spill last_value_ on every + // value, which costs two extra memory operations per value. + UT last = static_cast(last_value_); + const UT min_delta = static_cast(min_delta_); for (int j = 0; j < values_decode; ++j) { // Addition between min_delta, packed int and last_value should be treated as // unsigned addition. Overflow is as expected. - buffer[i + j] = static_cast(min_delta_) + static_cast(buffer[i + j]) + - static_cast(last_value_); - last_value_ = buffer[i + j]; + last += min_delta + static_cast(buffer[i + j]); + buffer[i + j] = last; } + last_value_ = static_cast(last); } values_remaining_current_mini_block_ -= values_decode; i += values_decode;