Skip to content

perf(array): reuse probe state in RLE, RunEnd and PCO - #9844

Open
joseph-isaacs wants to merge 6 commits into
developfrom
ji/array-probe-encodings
Open

joseph-isaacs wants to merge 6 commits into
developfrom
ji/array-probe-encodings

Conversation

@joseph-isaacs

@joseph-isaacs joseph-isaacs commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

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.

let mut probe = array.repeated_probe();
probe.execute_scalar(10, &mut ctx)?;
probe.execute_scalar(11, &mut ctx)?; // Reuses preparation at each visited slot.

Encodings

  • 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 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 with state.slot(i); the same body serves one-off and repeated reads. scalar_at delegates through ProbeState::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 scalar bench from #9896 now reads through one repeated probe, so CodSpeed tracks the clustered cases against that baseline.

Local medians, 1024 clustered lookups, execute_scalar per row (develop) vs a repeated probe:

array per-row repeated probe
PCO 5.54 ms 24 µs
PCO, nullable 4.17 ms 52 µs
RunEnd(PCO, PCO) 24.5 ms 4.1 ms
RLE, nullable 176 µs 133 µs

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-runend and vortex-pco, workspace clippy, formatting.

🤖 Generated with Claude Code

@codspeed

codspeed Bot commented Sep 11, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by ×10

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 6 improved benchmarks
✅ 2196 untouched benchmarks
⏩ 218 skipped benchmarks1

Performance Changes

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)

Open in CodSpeed

Footnotes

  1. 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.

@joseph-isaacs
joseph-isaacs added this pull request to stack #9860 September 14, 2026 12:02
@joseph-isaacs
joseph-isaacs force-pushed the ji/array-probe-encodings branch from bfd3004 to c99df0f Compare September 14, 2026 13:26
@joseph-isaacs
joseph-isaacs force-pushed the ji/array-probe-encodings branch from c99df0f to 05146d3 Compare September 14, 2026 14:04
@joseph-isaacs joseph-isaacs added the changelog/performance A performance improvement label Sep 14, 2026
@joseph-isaacs
joseph-isaacs marked this pull request as ready for review September 14, 2026 14:04
@joseph-isaacs
joseph-isaacs force-pushed the ji/array-probe-encodings branch 2 times, most recently from 05146d3 to 7c04bd8 Compare September 14, 2026 18:19
Base automatically changed from ji/array-probe-api to develop September 15, 2026 21:14
joseph-isaacs added a commit that referenced this pull request Sep 15, 2026
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
@joseph-isaacs
joseph-isaacs force-pushed the ji/array-probe-encodings branch from 7c04bd8 to ca55f9d Compare September 15, 2026 21:15
@joseph-isaacs joseph-isaacs changed the title perf(array): reuse probe state in primitive and compressed arrays perf(array): reuse probe state in RLE, RunEnd and PCO Sep 15, 2026
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
joseph-isaacs and others added 3 commits September 15, 2026 22:34
…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>
@joseph-isaacs
joseph-isaacs requested a review from myrrc September 16, 2026 08:21
joseph-isaacs and others added 2 commits September 16, 2026 09:30
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/performance A performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant