Skip to content

Add planner-owned selection and bounded ListLayout scans to V2 - #10537

Draft
joseph-isaacs wants to merge 2 commits into
developfrom
ji/decompression-v2-scan
Draft

joseph-isaacs wants to merge 2 commits into
developfrom
ji/decompression-v2-scan

Conversation

@joseph-isaacs

@joseph-isaacs joseph-isaacs commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Masked scans now apply selection through a planner before projection. ListLayout scans resolve selected offsets and validity before reading elements, and the projection planner can subdivide large fixed-width element ranges into independently scheduled work.

Based on develop 92a7db079. The planner owns subdivision, the shared 32 MiB decoded-work admission budget, child execution and ordered result collection. Small, repeated-segment, oversized-segment and unsupported work retain the serial pipeline.

  • PlannerOutput::Await lets the driver suspend a planner while its owned child tasks finish; existing Planner, Morsel, Source, IoSource, and SegmentSource methods are unchanged.
  • Selection-aware lazy lowering, bounded dense-list grouping and chunk-buffer reuse limit work on unselected rows.
  • Regression coverage includes outstanding IO during/after a planner await, cancellation, serial fallback, retired pipelines, and nullable fieldless structs.
  • Random access always uses the V2 scan builder for both local and object-store Vortex reads.

Validation before benchmark cleanup (c4fa7818b): 692 layout and 79 scan library tests passed, followed by all 7 await-protocol tests including one additional regression; scoped all-target/all-feature clippy passed with -D warnings; formatting used nightly-2026-09-10. That release binary passed 200 real-file V1/reference comparisons and five malformed-selection checks.

The final cleanup removes the experiment CI workflow, regression script, three diagnostic examples, their dependency, and benchmark reader-switch plumbing. Inspected the diff and searched for stale references; tests, builds and benchmarks were not rerun for this cleanup. The diagnostic scaffolding remains available in parent commit c4fa7818b.

Local benchmarks compare the pre-cleanup head c4fa7818b with develop 92a7db079, built with the same release_debug profile and default CPU flags on a 32-core ARM host. Random access covers all 18 standard targets at 1 and 30 cores, with two alternating 3-second passes and explicit V1/V2 readers. Compression covers all 15 default CPU datasets at 16 cores, with two alternating passes and five iterations per dataset; reported compression timings use the harness's best-of-five metric.

Comparison Geometric-mean latency change Largest increase
Random access V2 vs V1, 1 core -3.8% +9.6%
Random access V2 vs V1, 30 cores -7.7% +8.7%
Compression vs develop +0.6% +3.7%
Decompression vs develop -0.3% +3.0%

No standard random-access case regressed by more than 0.4% versus develop's V2. All 15 compressed sizes match. The small wide-table compression outlier did not persist: four alternating 30-iteration confirmations measured -0.4% encode / -1.7% decode for that case. Benchmark-only overlays expose V2 random access on develop and a V1 compression reader switch; no runtime baseline changes were made.

The planner refactor does retain an approximately 9–10% regression versus the rebased pre-refactor control for one large nested-list singleton with 1 MiB element chunks. Longer uninstrumented runs measured 0.241 ms before / 0.264 ms after. The cause is changed I/O batching and reduced parallel read overlap: a separate pread probe found 3.08 → 2.53 reads/query, ~3.2 MB combined reads in 14% → 31% of queries, active read concurrency 2.05 → 1.60, and time with reads active 89 → 112 µs/query. The extra ~23 µs accounts for the ~22 µs end-to-end gap. Read volume is effectively unchanged, CPU profiles have the same Arrow-conversion share (~33.6%), and the regression disappears with memory-backed input or one core.

This remains a draft. The 32 MiB estimate covers in-flight decoded work, not retained output or coalesced encoded buffers; the full memory matrix has not been revalidated for this version.

@codspeed

codspeed Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 22.54%

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

⚠️ 1 benchmark measured no execution time

Nothing ran under measurement, usually because the compiler removed the code under test. This result is not comparable, so it counts as unchanged.

Preventing compiler optimizations

⚠️ 17 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 40 benchmarks have no flame graph

The flamegraph could not be generated because we failed to resolve the symbols in the profiling trace. This happens when we fail to harvest information about language frames.

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

❌ 5 regressed benchmarks
✅ 2403 untouched benchmarks
⏩ 12 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ WallTime threshold_neon[8] 1.3 µs 2.3 µs -42.28%
❌ WallTime threshold_neon[64] 2.5 µs 3.3 µs -23.17%
❌ WallTime filter_neon[80] 2.5 µs 3.1 µs -19.79%
❌ WallTime bitpack_blocked_compress_avx2 6.7 µs 7.6 µs -11.76%
❌ ℹ️ Simulation test_scan_filter[int] 3 ms 3.4 ms -11.18%

Tip

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


Comparing ji/decompression-v2-scan (a8349ec) with develop (f661d4b)

Open in CodSpeed

Footnotes

  1. 12 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 changed the title perf(scan): compose selection planners and optimize V2 random access Add selection planners and ListLayout scans to V2 Oct 16, 2026
@joseph-isaacs
joseph-isaacs force-pushed the ji/decompression-v2-scan branch from 6b97c79 to 15f2fed Compare October 16, 2026 16:52
@joseph-isaacs
joseph-isaacs changed the base branch from claude/planner-decode-slice-migration-xppbf1 to ji/upbeat-volta-adp0xd October 16, 2026 16:52
Base automatically changed from ji/upbeat-volta-adp0xd to develop October 16, 2026 16:57
@joseph-isaacs
joseph-isaacs force-pushed the ji/decompression-v2-scan branch from 15f2fed to 02119f6 Compare October 16, 2026 17:00
joseph-isaacs added a commit that referenced this pull request Oct 16, 2026
Run the unchanged full 48-case Arrow comparison and 36-case standard
suite, including parent and candidate V1 controls for the standard suite.
Use the same CI-only wrapper as ccbe15b and baseline 5231b70.
Runtime source is 8442d46, tree-equivalent to local 95fda7fc8.

The previous run 38972129118 is complete. It confirmed the offset-copy
feature-vector improvements but retained one +5.2% Arrow rerun. This run
checks the combined offset-copy, generated-offset-statistics, and guarded
list-overlap changes. Keep PR #10537 draft; this run is not a merge gate.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
Rebase draft PR #10537 onto develop 92a7db0. Apply selections through a
composable planner, resolve list offsets before selected elements, and split
substantial fixed-width child projections at stored segment boundaries.
The projection planner owns subdivision, byte-budget admission, child task
execution and ordered collection. Unsupported splits retain the serial path.

Keep Planner and Morsel trait methods unchanged. Replace the experimental
completion callbacks, WorkFuture and ChildExecutor traits with one
PlannerOutput::Await variant; morsels only publish the assembled result.
Preserve develop's global local-file coalescing configuration. The 8 KiB
experiment caused tiny-lookup regressions and is not part of this version.

Preserve masked lazy lowering, bounded dense-list grouping, shared value
caches, validated chunk offsets and the fieldless nullable-struct fix.
Retain the diagnostic examples and regression benchmark workflow.

Backtracking: previous PR head 8442d46; experimental CI head 60fda25;
local pre-review checkpoint a253e5727 and reviewed checkpoint e8fd21a4a.
The byte budget covers in-flight decoded work, not retained output or
encoded/coalesced buffers. Full performance revalidation remains necessary.

Validation: 692 layout and 79 scan library tests passed, followed by all
7 await protocol tests including an additional await/IO regression.
Scoped all-target/all-feature clippy passed with -D warnings; formatted
with nightly-2026-09-10. Focused E2E measurements follow this checkpoint.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
@joseph-isaacs
joseph-isaacs force-pushed the ji/decompression-v2-scan branch from 8442d46 to c4fa781 Compare October 19, 2026 08:43
@joseph-isaacs joseph-isaacs changed the title Add selection planners and ListLayout scans to V2 Add planner-owned selection and bounded ListLayout scans to V2 Oct 19, 2026
Use the planner-driven V2 ScanBuilder directly for every Vortex random
access measurement, including object-store reads. Remove the reader toggle,
its configuration plumbing, V1 documentation, and unused reader-cache setup.

Remove the experiment-only regression workflow, driver script, and three
fixture/selection examples, along with their futures dev dependency.
Keep the planner implementation and regression tests. The previous commit
c4fa781 preserves the diagnostic harnesses and benchmark context.

Inspected the resulting diff and searched for stale switch/example
references. No tests, builds, or benchmarks were rerun for this cleanup.

Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
@joseph-isaacs joseph-isaacs added the action/bench-random-access Run only the random-access benchmark on this PR label Oct 19, 2026
@github-actions github-actions Bot removed the action/bench-random-access Run only the random-access benchmark on this PR label Oct 19, 2026
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Polar Signals Profiling Results

Latest Run

Status Commit Job Attempt Link
🟡 In Progress a8349ec compress-bench 1 Explore Profiling Data
🟡 In Progress a8349ec random-access-bench 1 Explore Profiling Data

Powered by Polar Signals Cloud

@github-actions

Copy link
Copy Markdown
Contributor

Benchmarks: Random Access 📖

Commits: PR a8349ec4 vs base 92a7db07
Verdict: No clear signal (environment too noisy confidence)
Attributed Vortex impact: -11.2%
Engines: random-access No clear signal (-11.2%, environment too noisy confidence)
Vortex (geomean): hot 0.897x ✅
Parquet (geomean): hot 1.010x ➖
Shifts: Parquet (control) +1.0% · Median polish -3.2%

How to read Verdict and Engines
  • Verdict: Overall PR-level signal after subtracting baseline drift estimated from Parquet control rows. It can be Likely improvement, Likely regression, or No clear signal.
  • Engines: Per-engine attribution. DataFusion is compared against DataFusion/Parquet controls; DuckDB is compared against DuckDB/Parquet controls. This answers whether each engine improved or regressed independently.
  • Confidence: Based on directional consistency, share of rows above the noise floor, and control-run noise.
  • Hot vs cold: Every measurement is run several times. The first run is reported as the cold run, and the median of the runs after it is reported as the hot run. The verdict and significance use hot runs; each target's geomean reports hot and cold beside each other where the individual runs were recorded and hot alone where they were not, the cold column shows first-run cost per row, and hot/cold is how much of each run the warm path saves. Rows whose results predate per-run reporting show only one value, taken from the value the runner reported.
  • Table cells: Each cell reads PR / base / %diff. The hot and cold columns mark a change as 🔴 slower or 🟢 faster once it clears this suite's threshold; anything smaller is noise here and is left unmarked, as is hot/cold, because a shift in the warm-up ratio is not a win or a loss by itself.

vortex / arrow-ipc / ns (1.062x ➖, 0↑ 1↓)
name ns (PR / base / %diff)
random-access/arrow-tokio-local-disk 74653 / 68607 / +8.8%
random-access/arrow-tokio-local-disk-footer 169125 / 168527 / +0.4%
random-access/feature-vectors/correlated/arrow-tokio-local-disk 2498435 / 2522808 / -1.0%
random-access/feature-vectors/correlated/arrow-tokio-local-disk-footer 3442879 / 3493249 / -1.4%
random-access/feature-vectors/uniform/arrow-tokio-local-disk 56020020 / 30138993 / +85.9% 🔴
random-access/feature-vectors/uniform/arrow-tokio-local-disk-footer 32606360 / 32308217 / +0.9%
random-access/nested-lists/correlated/arrow-tokio-local-disk 63272 / 60045 / +5.4%
random-access/nested-lists/correlated/arrow-tokio-local-disk-footer 107571 / 104170 / +3.3%
random-access/nested-lists/uniform/arrow-tokio-local-disk 1110466 / 1078953 / +2.9%
random-access/nested-lists/uniform/arrow-tokio-local-disk-footer 1196300 / 1097061 / +9.0%
random-access/nested-structs/correlated/arrow-tokio-local-disk 57564 / 54612 / +5.4%
random-access/nested-structs/correlated/arrow-tokio-local-disk-footer 98958 / 94861 / +4.3%
random-access/nested-structs/uniform/arrow-tokio-local-disk 928145 / 903216 / +2.8%
random-access/nested-structs/uniform/arrow-tokio-local-disk-footer 940632 / 927192 / +1.4%
random-access/taxi/correlated/arrow-tokio-local-disk 124422 / 124134 / +0.2%
random-access/taxi/correlated/arrow-tokio-local-disk-footer 226992 / 225219 / +0.8%
random-access/taxi/uniform/arrow-tokio-local-disk 2897748 / 2813735 / +3.0%
random-access/taxi/uniform/arrow-tokio-local-disk-footer 3000212 / 2962933 / +1.3%
random-access / vortex-file-compressed / ns (0.897x ✅, 7↑ 1↓)
name ns (PR / base / %diff)
random-access/feature-vectors/correlated/vortex-tokio-local-disk 178938 / 233649 / -23.4% 🟢
random-access/feature-vectors/correlated/vortex-tokio-local-disk-footer 1062923 / 1147837 / -7.4%
random-access/feature-vectors/uniform/vortex-tokio-local-disk 999963 / 1378299 / -27.4% 🟢
random-access/feature-vectors/uniform/vortex-tokio-local-disk-footer 2074376 / 2565372 / -19.1% 🟢
random-access/nested-lists/correlated/vortex-tokio-local-disk 156570 / 218309 / -28.3% 🟢
random-access/nested-lists/correlated/vortex-tokio-local-disk-footer 289686 / 353664 / -18.1% 🟢
random-access/nested-lists/uniform/vortex-tokio-local-disk 658652 / 677257 / -2.7%
random-access/nested-lists/uniform/vortex-tokio-local-disk-footer 855314 / 873786 / -2.1%
random-access/nested-structs/correlated/vortex-tokio-local-disk 224326 / 242718 / -7.6%
random-access/nested-structs/correlated/vortex-tokio-local-disk-footer 428738 / 437522 / -2.0%
random-access/nested-structs/uniform/vortex-tokio-local-disk 530534 / 586191 / -9.5%
random-access/nested-structs/uniform/vortex-tokio-local-disk-footer 766860 / 796521 / -3.7%
random-access/taxi/correlated/vortex-tokio-local-disk 630767 / 594014 / +6.2%
random-access/taxi/correlated/vortex-tokio-local-disk-footer 1179065 / 1199816 / -1.7%
random-access/taxi/uniform/vortex-tokio-local-disk 1754602 / 2343191 / -25.1% 🟢
random-access/taxi/uniform/vortex-tokio-local-disk-footer 2612479 / 3363875 / -22.3% 🟢
random-access/vortex-tokio-local-disk 505883 / 387324 / +30.6% 🔴
random-access/vortex-tokio-local-disk-footer 846984 / 871456 / -2.8%
random-access / parquet / ns (1.010x ➖, 1↑ 1↓)
name ns (PR / base / %diff)
random-access/feature-vectors/correlated/parquet-tokio-local-disk 106819802029 / 106355839970 / +0.4%
random-access/feature-vectors/correlated/parquet-tokio-local-disk-footer 112208630830 / 113042205755 / -0.7%
random-access/feature-vectors/uniform/parquet-tokio-local-disk 107270930463 / 132160209457 / -18.8% 🟢
random-access/feature-vectors/uniform/parquet-tokio-local-disk-footer 162806124179 / 107764144489 / +51.1% 🔴
random-access/nested-lists/correlated/parquet-tokio-local-disk 144349649 / 143249847 / +0.8%
random-access/nested-lists/correlated/parquet-tokio-local-disk-footer 148540528 / 145152372 / +2.3%
random-access/nested-lists/uniform/parquet-tokio-local-disk 148084888 / 145444723 / +1.8%
random-access/nested-lists/uniform/parquet-tokio-local-disk-footer 144408452 / 147220794 / -1.9%
random-access/nested-structs/correlated/parquet-tokio-local-disk 44568846 / 44732303 / -0.4%
random-access/nested-structs/correlated/parquet-tokio-local-disk-footer 44197209 / 44823933 / -1.4%
random-access/nested-structs/uniform/parquet-tokio-local-disk 44530768 / 44715601 / -0.4%
random-access/nested-structs/uniform/parquet-tokio-local-disk-footer 44675271 / 45113025 / -1.0%
random-access/parquet-tokio-local-disk 118748922 / 119938138 / -1.0%
random-access/parquet-tokio-local-disk-footer 118597690 / 118538394 / +0.1%
random-access/taxi/correlated/parquet-tokio-local-disk 178075472 / 179834753 / -1.0%
random-access/taxi/correlated/parquet-tokio-local-disk-footer 177765960 / 177573135 / +0.1%
random-access/taxi/uniform/parquet-tokio-local-disk 187356504 / 192945410 / -2.9%
random-access/taxi/uniform/parquet-tokio-local-disk-footer 192339158 / 187342599 / +2.7%
random-access / lance / ns (1.000x ➖, 0↑ 0↓)
name ns (PR / base / %diff)
random-access/feature-vectors/correlated/lance-tokio-local-disk 214068 / 209684 / +2.1%
random-access/feature-vectors/correlated/lance-tokio-local-disk-footer 979288 / 1009277 / -3.0%
random-access/feature-vectors/uniform/lance-tokio-local-disk 1038731 / 1065477 / -2.5%
random-access/feature-vectors/uniform/lance-tokio-local-disk-footer 1841137 / 1894783 / -2.8%
random-access/lance-tokio-local-disk 553012 / 548108 / +0.9%
random-access/lance-tokio-local-disk-footer 1325905 / 1317622 / +0.6%
random-access/nested-lists/correlated/lance-tokio-local-disk 158802 / 159403 / -0.4%
random-access/nested-lists/correlated/lance-tokio-local-disk-footer 496896 / 495934 / +0.2%
random-access/nested-lists/uniform/lance-tokio-local-disk 890134 / 915993 / -2.8%
random-access/nested-lists/uniform/lance-tokio-local-disk-footer 1415220 / 1423728 / -0.6%
random-access/nested-structs/correlated/lance-tokio-local-disk 278932 / 278613 / +0.1%
random-access/nested-structs/correlated/lance-tokio-local-disk-footer 566519 / 561231 / +0.9%
random-access/nested-structs/uniform/lance-tokio-local-disk 2384916 / 2270739 / +5.0%
random-access/nested-structs/uniform/lance-tokio-local-disk-footer 2592402 / 2646167 / -2.0%
random-access/taxi/correlated/lance-tokio-local-disk 789913 / 790786 / -0.1%
random-access/taxi/correlated/lance-tokio-local-disk-footer 1832642 / 1825701 / +0.4%
random-access/taxi/uniform/lance-tokio-local-disk 8112368 / 8031996 / +1.0%
random-access/taxi/uniform/lance-tokio-local-disk-footer 8899601 / 8661295 / +2.8%

@joseph-isaacs joseph-isaacs added action/bench-random-access Run only the random-access benchmark on this PR action/bench-compress Run only the compression benchmark on this PR labels Oct 19, 2026
@github-actions github-actions Bot removed action/bench-compress Run only the compression benchmark on this PR action/bench-random-access Run only the random-access benchmark on this PR labels Oct 19, 2026

This branch has not been deployed

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

Labels

changelog/feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant