Skip to content

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

Merged
joseph-isaacs merged 8 commits into
developfrom
ji/array-probe-encodings
Sep 22, 2026
Merged

joseph-isaacs merged 8 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 regress 5 benchmarks

⚠️ 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
❌ 5 regressed benchmarks
✅ 2197 untouched benchmarks
⏩ 293 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation decode_primitives[f32, (1000, 512)] 42 µs 64.2 µs -34.61%
WallTime dbp_assemble_kernel_avx512[(I128, 1024)] 467 ns 559 ns -16.46%
WallTime filtered_owned_i64_avx2[OneNullInEight] 22.4 µs 25.8 µs -13.15%
Simulation take_fsl_u32_random[128, 100] 208.2 µs 237.2 µs -12.25%
Simulation take_fsl_f16_random[256, 100] 204.8 µs 230.6 µs -11.19%
Simulation scalar_access[(1024, false, false)] 135,445.9 µs 791.7 µs ×170
Simulation scalar_access[(1024, true, false)] 97 ms 1.3 ms ×78
Simulation scalar_access[(1024, false, true)] 135.5 ms 2.8 ms ×48
Simulation take_fsl_u32_random[256, 10] 159.4 µs 124.4 µs +28.22%
WallTime dbp_assemble_kernel_avx2[(I128, 1024)] 531 ns 464 ns +14.44%
Simulation take_fsl_random[128, 10] 139.4 µs 126.6 µs +10.1%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing ji/array-probe-encodings (96bb450) with develop (24d67e8)

Open in CodSpeed

Footnotes

  1. 293 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>
Comment thread encodings/fastlanes/src/rle/probe.rs
Comment thread encodings/fastlanes/src/rle/probe.rs
Comment thread encodings/fastlanes/src/rle/probe.rs
Comment thread encodings/pco/src/probe.rs Outdated
Comment thread encodings/pco/src/probe.rs Outdated

@myrrc myrrc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

joseph-isaacs and others added 2 commits September 22, 2026 10:30
`last_page` defaulted to 0, which claimed a previous probe had landed in
page 0 before any probe had run and indexed `pages` before it was known to
be non-empty. Making it `Option<usize>` states that there is no last page
until one is resolved.

The page lookup that follows can only miss on an out-of-range value index,
which the mask and page table rule out, so it becomes a `vortex_expect`
with a static message rather than an error.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01THeokTcUrxdbZcPrDy8x6D
The page decompressor writes every element of the page, so zeroing the
buffer first is a wasted pass over it. Reserve the capacity and set the
length instead, as the range decompressor already does.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01THeokTcUrxdbZcPrDy8x6D
@joseph-isaacs
joseph-isaacs force-pushed the ji/array-probe-encodings branch from 2bc65ec to 96bb450 Compare September 22, 2026 10:31
@joseph-isaacs
joseph-isaacs merged commit 00a0ccc into develop Sep 22, 2026
86 of 87 checks passed
@joseph-isaacs
joseph-isaacs deleted the ji/array-probe-encodings branch September 22, 2026 16:27
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.

2 participants