Repository navigation
Commit 9ba8442
feat(parquet): add the DELTA_LENGTH_BYTE_ARRAY nz map consumer
Third of the nz map series. Still opt-in behind `LIBCUDF_PARQUET_NZ_MAP=1`.
`decode_delta_length_byte_array_kernel_with_nz_map` decodes flat
DELTA_LENGTH_BYTE_ARRAY pages using the nz map instead of decoding definition
levels. Its decode loop is the producer/writer pipeline of the DELTA_BINARY
consumer from #24501: warp 0 decodes a batch of lengths into the decoder's
rolling buffer while warp 1 writes the batch before it, placing each length at
the row the map gives. The rest of the kernel follows
`decode_delta_length_byte_array_kernel`: page setup, the skip-resume path that
finds a slice's starting byte offset, the null zero-fill, the offsets scan and
the character copy.
The level-decoding kernel computed three things as side effects of
`gpuDecodeLevels`, which the consumer now computes directly: the null count,
which is `num_rows` minus the map's valid values at or after `first_row`; the
row count the offsets scan covers; and the zero-fill's range, which is
`num_rows`.
`classify_nz_map_kind` now also selects DELTA_LENGTH_BYTE_ARRAY pages. Routing
matches the DELTA_BINARY pair: the level-decoding kernel's skip is gated on the
same flag that decides whether the consumer is launched. The host now records
which encodings still have a page without a map, in `unmapped_kernel_mask`, so
each level-decoding kernel is launched only when its encoding has work for it
rather than on one bool scoped to DELTA_BINARY.
Each consumer now runs on its own stream, forked from the producer's with
`fork_streams` and joined back with `join_streams`. That orders it after the
producer while the two consumers overlap with each other and with the
level-decoding kernels. Sharing the producer's stream, as the DELTA_BINARY
consumer did on its own, would run the two consumers one after the other.
Both nz map consumers now reject a page whose map asks for more values than its
delta header declares, before writing anything, and report
`DATA_STREAM_OVERRUN`. The decoder's cursor advances a mini-block at a time and
can pass the end of a short stream, so the writer read rolling-buffer slots the
page never wrote and the read succeeded with values that were never decoded.
That was already true of the DELTA_BINARY_PACKED consumer from #24501, which is
fixed here too. `nz_count` counts from the start of the page, so it is the
number of values a read needs, and a bounded read that uses fewer values than
the page declares is unaffected. The DLBA consumer also returns before the
offsets scan if its loop stops short, rather than scanning unwritten lengths.
`init_binary_block` now rejects a block size or value count above INT32_MAX.
Both are uleb128s, so 64 bits wide, while the decoder steps its 32-bit
`current_value_idx` by `values_per_mb` until it reaches `value_count`. A block
size of 2^32 with one mini-block narrows `values_per_mb` to zero, and a block
size of 2^31 with a value count above it wraps the index back to zero; either
way `find_end_of_block` never finishes. The DELTA_LENGTH_BYTE_ARRAY and
DELTA_BYTE_ARRAY decoders call it, as does the string-size pass that runs for
those pages whether or not the nz map is on. Parquet stores a page's value count
as int32, so no valid page exceeds either bound. The DELTA_BINARY_PACKED
decoders never call it and decoded garbage rather than hanging; they now report
the header as unsupported.
A mini-block's byte length is now computed in 64 bits. The bitwidth comes from
the page unvalidated, so up to 255, and `values_per_mb` is bounded only by what
fits in 32, so `bitwidth * values_per_mb / 8` could wrap and place the next
mini-block's start below the block, where the `block_end` checks still pass and
the unpack reads the wrong bytes.
Tests: `NzMapMatchesLevelDecoder` gains a nullable DELTA_LENGTH_BYTE_ARRAY
column, without which its consumer is never selected. `NzMapNarrowDeltaBlock`
reads DELTA blocks of 32 and 64 values, narrower than the spec allows but
accepted by `init_binary_block`, and a 256-value mini-block that takes more than
one pass per batch, with the nz map off and on.
`NzMapRejectsShortDeltaStream` builds pages that ask for more values than their
stream declares, for both consumers, plus controls that read a stream holding
exactly enough and a bounded read holding more than it needs.
`DeltaHeaderCountsTooLarge` reads three oversized headers through both encodings
with the nz map off and on.
The two 512 MiB delta benchmarks gain a `validity` axis. The existing nullable
sweep is 8 MiB, which is L2-resident on an H100, so the nz map's extra traffic of
4 bytes per value could not show up there -- and for a string column that cost is
carried against a much larger payload than the fixed-width case. The nested path
builds its own data profile and ignores `validity`, so those cells are skipped
rather than run three times over on identical data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>1 parent dac5249 commit 9ba8442
8 files changed
Lines changed: 505 additions & 62 deletions
File tree
- cpp
- benchmarks/io/parquet
- src/io/parquet
- tests/io
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
129 | 135 | | |
130 | 136 | | |
131 | 137 | | |
| |||
185 | 191 | | |
186 | 192 | | |
187 | 193 | | |
| 194 | + | |
188 | 195 | | |
189 | 196 | | |
190 | 197 | | |
| |||
195 | 202 | | |
196 | 203 | | |
197 | 204 | | |
| 205 | + | |
198 | 206 | | |
199 | 207 | | |
200 | 208 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
176 | 177 | | |
177 | 178 | | |
178 | 179 | | |
179 | | - | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
180 | 188 | | |
181 | 189 | | |
182 | 190 | | |
| |||
197 | 205 | | |
198 | 206 | | |
199 | 207 | | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
200 | 215 | | |
201 | 216 | | |
202 | 217 | | |
| |||
209 | 224 | | |
210 | 225 | | |
211 | 226 | | |
212 | | - | |
| 227 | + | |
213 | 228 | | |
214 | 229 | | |
215 | 230 | | |
216 | 231 | | |
217 | | - | |
| 232 | + | |
218 | 233 | | |
219 | 234 | | |
220 | 235 | | |
| |||
0 commit comments