perf(array): reuse probe state in RLE, RunEnd and PCO - #9844
joseph-isaacs wants to merge 6 commits into
Conversation
Merging this PR will improve performance by ×10
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | scalar_access[(1024, false, false)] |
132,772 µs | 769 µs | ×170 |
| ⚡ | Simulation | scalar_access[(1024, true, false)] |
100.5 ms | 1.2 ms | ×81 |
| ⚡ | Simulation | scalar_access[(1024, false, true)] |
132.6 ms | 2.9 ms | ×46 |
| ⚡ | WallTime | arrow_checked_add_u32_neon[16384] |
20.4 µs | 13.6 µs | +50.12% |
| ⚡ | Simulation | decompress[u64, (4000, 1024)] |
86.7 µs | 71.1 µs | +21.84% |
| ⚡ | WallTime | mul_u32_nonnull_avx512 |
6.2 µs | 5.6 µs | +11.99% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing ji/array-probe-encodings (07dbdae) with develop (5cc630b)
Footnotes
-
218 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
bfd3004 to
c99df0f
Compare
c99df0f to
05146d3
Compare
05146d3 to
7c04bd8
Compare
Port of #9844 onto the reduced probe API. Encodings implement one `probe_scalar(state, index, ctx)` and read children through `state.slot(i)`; the same body serves one-off and repeated reads. - FastLanes RLE: `RleProbeState` keeps the slice's base value offset. `state.split()` holds it while the indices, offsets and values children are read through `ProbeChildren::slot`. - RunEnd: the ends child probe is held across the binary search, so a repeated read keeps the child's preparation within and between searches; the values child is probed for the selected run. - PCO: `PcoProbeState` keeps the validity mask, prefix ranks for non-null positions, page boundaries and the most recently decoded page. A one-off read decompresses the single row as before. - Tests cover one-off and repeated access over sliced and nullable inputs, nested RunEnd(PCO, RunEnd(PCO, PCO)) state reuse and drop counts, lazy validity, and page-cache eviction. The PCO crate gains a scalar-probe benchmark and an example. Short local run of the new bench, medians, 1024 clustered lookups: PCO 5.54 ms via `execute_scalar` vs 24 µs via a repeated probe; RunEnd(PCO, PCO) 24.5 ms vs 4.1 ms; RLE nullable 176 µs vs 133 µs. Scattered PCO lookups are unchanged, each landing on a new page. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7
7c04bd8 to
ca55f9d
Compare
Port of #9844 onto the reduced probe API. Encodings implement one `probe_scalar(state, index, ctx)` and read children through `state.slot(i)`; the same body serves one-off and repeated reads. - FastLanes RLE: `RleProbeState` keeps the slice's base value offset. `state.split()` holds it while the indices, offsets and values children are read through `ProbeChildren::slot`. - RunEnd: the ends child probe is held across the binary search, so a repeated read keeps the child's preparation within and between searches; the values child is probed for the selected run. - PCO: `PcoProbeState` keeps the validity mask, prefix ranks for non-null positions, page boundaries and the most recently decoded page. A one-off read decompresses the single row as before. - Tests cover one-off and repeated access over sliced and nullable inputs, nested RunEnd(PCO, RunEnd(PCO, PCO)) state reuse and drop counts, lazy validity, and page-cache eviction. The PCO crate gains a scalar-probe benchmark and an example. Short local run of the new bench, medians, 1024 clustered lookups: PCO 5.54 ms via `execute_scalar` vs 24 µs via a repeated probe; RunEnd(PCO, PCO) 24.5 ms vs 4.1 ms; RLE nullable 176 µs vs 133 µs. Scattered PCO lookups are unchanged, each landing on a new page. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7
ca55f9d to
f1008f3
Compare
…odings-v2 Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> # Conflicts: # encodings/pco/Cargo.toml
Drops the probe bench added by this PR; the scalar bench from #9896 is the baseline, and reading it through one `RepeatedArrayProbe` shows the retained page decode on the clustered cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7 Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Each page is decoded at most once per probe, so scattered reads stop paying a page decode per row. Storage grows with the pages touched and never beyond the decompressed array. The last page is checked before the binary search so clustered reads keep their cost. Local medians, 1024 lookups, before vs after: scattered 5.4 ms vs 122 µs; clustered 25 µs vs 24 µs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7 Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Removes the example and the unused fastlanes dev-dependency, and the `cfg(test)` counters on `PcoProbeState`; the tests that read them are replaced by behavioural checks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7 Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Drops the runend dev-dependency; random access is covered over nullable, sliced and repeated PCO arrays directly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016CqrLKgPqFYGZK5sjk1qe7 Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Uses the scalar probe API from #9843 to retain preparation across repeated lookups, including recursive child access. Stacked on #9843; the diff is one commit.
Encodings
RleProbeStatekeeps the slice's base value offset.state.split()holds it while the indices, offsets and values children are read throughProbeChildren::slot.PcoProbeStatekeeps the validity mask, prefix ranks for non-null positions, page boundaries and every page decoded so far, so each page is decoded at most once per probe. A one-off read decompresses the single row as before.Each encoding implements
probe_scalar(state, index, ctx)once and reads children withstate.slot(i); the same body serves one-off and repeated reads.scalar_atdelegates throughProbeState::once.Tests and benchmarks
One-off and repeated access over sliced and nullable inputs; RunEnd and RLE state reuse; PCO physical types, all-null rows and probes that outlive their source. The PCO
scalarbench from #9896 now reads through one repeated probe, so CodSpeed tracks the clustered cases against that baseline.Local medians, 1024 clustered lookups,
execute_scalarper row (develop) vs a repeated probe:Scattered PCO lookups over 16 pages go from 5.4 ms to 122 µs, one decode per page; single lookups are equal between the two paths.
Validation: tests for
vortex-fastlanes,vortex-runendandvortex-pco, workspace clippy, formatting.🤖 Generated with Claude Code