Skip to content

perf(array): reuse probe state in primitive and compressed arrays - #9844

Draft
joseph-isaacs wants to merge 1 commit into
ji/array-probe-apifrom
ji/array-probe-encodings
Draft

perf(array): reuse probe state in primitive and compressed arrays#9844
joseph-isaacs wants to merge 1 commit into
ji/array-probe-apifrom
ji/array-probe-encodings

Conversation

@joseph-isaacs

Copy link
Copy Markdown
Contributor

Summary

Reuse preparation across random scalar reads for Primitive, FastLanes RLE, RunEnd, and PCO. This is the second PR in the stack, based on #9843; review that PR for the API and this diff for the encoding implementations. Existing execute_scalar remains unchanged.

Changes

  • Primitive retains materialized validity or uses a retained child probe to evaluate lazy validity only at requested rows.
  • RLE retains direct readers for materialized primitive slots and the slice base offset. Encoded children use the context's lazy slot probes; local state stores slot IDs rather than child probes.
  • RunEnd uses unit local state and the context's ends/value probes, preserving preparation across binary-search comparisons and lookups. The selected value supplies nullness too.
  • PCO retains validity, non-null prefix ranks, page boundaries, and its most recently decoded page.
  • Add nullable/sliced/empty/lazy-error coverage, a recursive RunEnd(PCO, RunEnd(PCO, PCO)) regression that counts state initialization, decoding, and destruction, and an example plus paired benchmarks.

Once creates no retained state. Other encodings, including ScalarFn and Zstd, continue to use the default probe hook from #9843. There are no further public API changes in this layer.

Performance

Local ARM64 macOS medians for 1,024 clustered, non-null reads, including preparation and teardown:

Encoding execute_scalar Probe Speedup
RLE 101 µs 20.91 µs 4.8×
PCO 5.419 ms 21.08 µs 257×
RunEnd(PCO, PCO) 34.26 ms 3.999 ms 8.6×

PCO's one-page cache helps scattered reads much less (5.412 ms → 4.91 ms in the corresponding non-null case). One-off non-null RLE adds about 6.5 ns. Full nullable/scattered results and reproduction commands are in encodings/pco/benches/probe.md. Primitive, RunEnd, and PCO contexts fit inline on this build; RLE spills once on first repeated use.

Validation

  • cargo nextest run -p vortex-array -p vortex-fastlanes -p vortex-pco -p vortex-runend: 3,922 passed, one skipped.
  • Doctests and Clippy with all targets/features for those four crates: passed.
  • Example and serial benchmark measurements: results recorded in encodings/pco/benches/probe.md.
  • Workspace Clippy was attempted and blocked by the nvCOMP build script downloading the NVIDIA archive in this environment.
  • cargo +nightly fmt --all and git diff --check: passed.

Cargo checks use RUSTC_WRAPPER= after the sandbox's sccache permission failure.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
@codspeed-hq

codspeed-hq Bot commented Sep 11, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 17.03%

⚠️ 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

⚡ 2 improved benchmarks
❌ 12 regressed benchmarks
✅ 2183 untouched benchmarks
🆕 64 new benchmarks
⏩ 218 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime arrow_checked_add_u32_neon[16384] 13.5 µs 20.4 µs -34.15%
WallTime filtered_sink_i64_avx2[OneNullInEight] 21.9 µs 31.2 µs -29.76%
WallTime mul_u64_nonnull_neon 15.1 µs 21.5 µs -29.66%
WallTime filtered_sink_i64_avx512[OneNullInEight] 22.3 µs 31.5 µs -29.4%
WallTime filtered_sink_i64_avx2[NineNullsInTen] 13.1 µs 16.6 µs -20.97%
WallTime filtered_sink_i64_avx512[NineNullsInTen] 13.5 µs 16.7 µs -19.53%
Simulation new_raw_prim_test_between[i32, 2048] 62.1 µs 76.7 µs -18.99%
WallTime dict_canonicalize_gt_u8_avx512[16000000] 6.8 ms 8.2 ms -17.79%
WallTime mul_i64_nonnull_neon 17.2 µs 20.6 µs -16.47%
WallTime multiply_shapes_neon[(16384, PerRowPerRow)] 17.3 µs 20.6 µs -16.08%
Simulation allocate_drop_arrow[0] 402.7 ns 456.9 ns -11.86%
WallTime words_gather_scalar_avx2[65536] 8.3 µs 9.4 µs -11.83%
Simulation decompress[u64, (4000, 1024)] 85.9 µs 70.1 µs +22.49%
WallTime mul_u32_nonnull_avx512 6.3 µs 5.6 µs +12.87%
🆕 Simulation pco_execute_scalar[(1, false, false)] N/A 211.9 µs N/A
🆕 Simulation pco_execute_scalar[(1, true, false)] N/A 207.5 µs N/A
🆕 Simulation pco_execute_scalar[(1024, false, false)] N/A 132.5 ms N/A
🆕 Simulation pco_execute_scalar[(1024, false, true)] N/A 132.7 ms N/A
🆕 Simulation pco_execute_scalar[(1024, true, false)] N/A 100.7 ms N/A
🆕 Simulation pco_execute_scalar[(1024, true, true)] N/A 98.4 ms N/A
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

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 (bfd3004) with ji/array-probe-api (8146a99)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant