Skip to content

[WIP][POC] GH-51268: [C++][Parquet] Scan DELTA_BINARY_PACKED deltas a vector at a time - #51295

Draft
prtkgaur wants to merge 3 commits into
apache:mainfrom
prtkgaur:gh51268-dbp-vector-prefix-sum
Draft

prtkgaur wants to merge 3 commits into
apache:mainfrom
prtkgaur:gh51268-dbp-vector-prefix-sum

Conversation

@prtkgaur

@prtkgaur prtkgaur commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #51250.

Rationale for this change

Prefix sum: a chain of dependent additions. A log-step inclusive scan computes it a register at a
time, in log2(lanes) shifted additions. Adding the frame of reference before the scan turns
its running multiple into a term the scan produces, rather than a multiply per lane.

What changes are included in this PR?

I replaced the sequential addition loop with a vectorized prefix sum (scan) to speed up decoding.

Are these changes tested?

Yes. I added a new typed test that explicitly forces the decoder through the vector loop, the scalar remainder loop, and the hand-off between them to ensure totals and wrapping behave correctly.

Benchmark

Same setup as #51250. NarrowSorted is added by this PR and holds non-decreasing values,
the shape DELTA_BINARY_PACKED is usually chosen for.

benchmark #51250 this PR vs. main
Decode_Int32_NarrowSorted 73.7 us 45.7 us 1.61x 2.18x
Decode_Int32_Narrow 74.8 us 46.4 us 1.61x 2.15x
Decode_Int32_Wide 83.7 us 53.6 us 1.56x 1.90x
Decode_Int64_NarrowSorted 71.4 us 55.0 us 1.30x 1.47x
Decode_Int64_Narrow 70.9 us 55.2 us 1.28x 1.49x
Decode_Int64_Wide 318.6 us 301.3 us 1.06x 1.07x
Decode_Int32_Fixed 20.4 us 20.5 us 1.00x 0.97x
Decode_Int64_Fixed 31.3 us 31.3 us 1.00x 0.98x

Are there any user-facing changes?

No. No API change, no format change, and decoded values are identical.

@prtkgaur
prtkgaur force-pushed the gh51268-dbp-vector-prefix-sum branch 6 times, most recently from 83a5289 to 5f91b9b Compare September 18, 2026 21:09
@prtkgaur
prtkgaur force-pushed the gh51268-dbp-vector-prefix-sum branch 2 times, most recently from 4e3aaeb to 3e46ef0 Compare October 4, 2026 02:36
@prtkgaur prtkgaur changed the title GH-51268: [C++][Parquet] Scan DELTA_BINARY_PACKED deltas a vector at a time [WIP][POC] GH-51268: [C++][Parquet] Scan DELTA_BINARY_PACKED deltas a vector at a time Oct 4, 2026
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

Thanks for opening a pull request!

This pull request has been automatically converted to a draft because its title doesn't match Arrow's required format.

If this is not a minor PR, could you open an issue for this pull request on GitHub? https://github.com/apache/arrow/issues/new/choose

Opening GitHub issues ahead of time contributes to the Openness of the Apache Arrow project.

Then could you also rename the pull request title in the following format?

GH-${GITHUB_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}

or

MINOR: [${COMPONENT}] ${SUMMARY}

After updating the title, you can mark the pull request as ready for review.

See also:

The reconstruction loop stores to an output buffer of the same type as
the frame and the running value, so the compiler has to assume a store
can change either member and reloads both for every value. The loop now
copies them to locals and writes the running value back once at the
end. The arithmetic stays unsigned, so overflow still wraps.
The decoder unpacked one miniblock per call, which at the writer's
default block sizes is a call for every 32 int32 or 64 int64 values,
and at narrow widths the per-call setup is a large share of the work.
Each miniblock holds a multiple of 32 values, so adjacent miniblocks of
one width form a single bitstream. The decoder now unpacks such a run
in one call, bounded by the current block and by the number of values
requested. Zero-width miniblocks keep their own path. A new test covers
where runs start and stop, and a read batch of 100 values ends partway
through one.
Rebuilding values from deltas is a chain of dependent additions. An
inclusive scan does a register's worth in log2(lanes) shifted additions,
and adding the frame before the scan makes its running multiple come out
of the scan instead of costing a multiply per lane. The running total
moves between batches in a vector register, because reading it out to a
general-purpose register would put a round trip on the dependency chain.
Registers of fewer than four lanes keep the scalar loop, where the scan
is slower; at the 128-bit baseline that includes all of int64. A new
benchmark input holds non-decreasing values, and a new test decodes
every delta width through the vector loop, the scalar remainder and the
hand-off between them.
@prtkgaur
prtkgaur force-pushed the gh51268-dbp-vector-prefix-sum branch from 3e46ef0 to 642c05e Compare October 8, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants