You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ba5fcfc
Browse filesBrowse the repository at this point in the historyBrowse files
Blocked FoR with one reference per 1024-element chunk (#10105)
Stacked on #10104.
## Summary
The in-memory `FoR` array now allows one reference per 1024-element
chunk instead of a single global reference per array.
This is supported by adding a `references` child that records each
reference. Element `i` decodes as `encoded[i] + references[(offset + i)
/ 1024]` with wrapping arithmetic.
This also requires adding an `offset` pointer. This is the position of
the first element within the first chunk and only matters for sliced
arrays.
The wire format is unchanged. `fastlanes.for` reads its single reference
as a `ConstantArray` child, and `FoRPlugin` writes `fastlanes.for` only
when the references are constant.
Note that FoR encoding always uses a constant reference per array still
and serialization refuses arrays that have non-constant references, so
this change will not lead to breaks.
## Example
### References
A 3000-row array spans three chunks, each with its own reference.
`encoded` holds each value minus its chunk's reference.
```text
rows 0 ─────────── 1023 │ 1024 ────────── 2047 │ 2048 ──────── 2999
references [ 1_000_000 │ 5_000_000 │ 9_000_000 ]
encoded [ small values │ small values │ small values ]
value[1500] = encoded[1500] + references[1500 / 1024]
= encoded[1500] + references[1]
= encoded[1500] + 5_000_000
```
### Offset
Slicing is zero-copy, so a slice can start partway through a chunk.
`offset` records how far into its first chunk the slice starts.
```text
│◄──── chunk 0 ────►│◄──── chunk 1 ────►│◄──── chunk 2 ────►│
rows 0 1024 1500 2048 2500 3000
slice [═══════════════════)
│◄ 476 ►│
slice(1500..2500):
encoded = encoded.slice(1500..2500) 1000 rows
references = references.slice(1..3) [5_000_000, 9_000_000]
offset = 1500 % 1024 476
slice row i uses references[(476 + i) / 1024]:
i = 0..=547 → references[0] = 5_000_000 (original chunk 1)
i = 548..=999 → references[1] = 9_000_000 (original chunk 2)
```
The new offset is always below 1024. Slicing a slice adds the offsets
and slices the references again.
### Constant references (existing files)
```text
fastlanes.for on disk metadata = reference 42, children = [encoded]
│ read ▲ write (only when references are constant)
▼ │
in memory references = ConstantArray(42, len = num_chunks), offset = 0
```
## API
- `FoRSlots` gains `references`.
- `FoRData` holds only `offset: u16`. The stored reference scalar is
gone.
- `FoRArrayExt::reference_scalar() -> &Scalar` is replaced by
`constant_reference() -> Option<Scalar>`, which is `Some` when
`references` is a `ConstantArray`. `offset()` and `ptype()` move onto
the extension trait.
- `FoR::try_new(encoded, reference)` keeps its signature and builds the
constant child. `FoR::try_new_chunked(encoded, references, offset)`
accepts any references.
## Kernels
With constant references, every kernel takes its existing path. That
includes Eq/NotEq compare pushdown, is_constant, is_sorted, take,
filter, the fused BitPacked decode, and CUDA FFOR and dynamic dispatch.
With varying references:
- Decoding adds each chunk's reference in place. `scalar_at` looks up
the element's chunk reference. `slice` slices the references by chunk
and carries the new offset. `cast` casts the references.
- Compare, is_constant, is_sorted, take and filter return `None` and
fall back to decoding.
- CUDA decodes these arrays on the CPU and never fuses them into a
dispatch plan.
Callers that rewrap a FoR's encoded child (the btrblocks FoR scheme,
benches, CUDA tests) now pass through `references()` and `offset()` via
`try_new_chunked`.
---------
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
0 commit comments