Skip to content

feat(scan): let a scan request bound the splits it decodes at once - #11127

Merged
robert3005 merged 7 commits into
vortex-data:developfrom
quantcast:cwirt/upstream-scan-split-concurrency
Nov 9, 2026
Merged

robert3005 merged 7 commits into
vortex-data:developfrom
quantcast:cwirt/upstream-scan-split-concurrency

Conversation

@ChristopherWirt

Copy link
Copy Markdown
Contributor

Summary

A scan decodes 4 × available_parallelism() splits at once. In Spark, every task in an executor runs its own scan sized to the whole machine, and each split holds the decoded chunks of every column it overlaps. On wide, nested files that is GiBs of native memory per executor (#11123).

This adds a per-scan limit on how many splits are decoded at once.

Layer API
Rust ScanRequest::split_concurrency → ScanBuilder::with_some_max_splits_in_flight
JNI ScanOptions.splitConcurrency()
Spark vortex.splitConcurrency read option

Result: a full scan of a 180 MB list-layout file, peak live native bytes:

Splits at once Peak live Time
48 (default, 4 × 12 cores) 3.65 GiB 1.0 s
12 1.18 GiB 1.0 s

Changes

  • vortex-scan / vortex-layout: ScanRequest::split_concurrency, honoured by both layout data sources.
    • It is a cap, separate from the per-worker with_concurrency. A later with_concurrency call (as the DataFusion opener makes) leaves it in place, and concurrency() keeps its meaning.
    • Per-worker scaling saturates before the cap is applied, so it can't overflow.
  • JNI: ScanOptions.splitConcurrency().
    • A capped scan drives its Arrow stream directly, not through the runtime's result queue, which can hold one extra finished batch per core. The Arrow conversion done ahead of Java is limited by the same value. Uncapped scans keep the existing queue.
  • Spark: the vortex.splitConcurrency read option, for V1 and V2 reads, documented in docs/user-guide/spark.md.
  • It doesn't limit I/O: segment reads for later splits may still be coalesced ahead of them. The docs say so.

Tests

  • Rust: a scan of 64 one-row splits decodes exactly 1, and exactly 2, at once with the cap set, against 48 by default. A later with_concurrency keeps the cap. Concurrency scaling saturates instead of overflowing.
  • JNI: ScanOptions validation, and a capped scan reads every row. Paused-consumer regressions with two background workers.
  • Spark: option parsing, validation and serialization, plus V1 and V2 reads with the option set.

On the fork (quantcast#64, same change), the Rust tests for vortex-layout, vortex-scan, vortex-jni and vortex-file passed, along with the JNI, Spark 3.5 and Spark 4.0 suites.

This branch ports that change onto develop. The fork's Spark V1/V2 read test lived in a test class that develop doesn't have, so it moved into VortexV1FallbackTest. Only git diff --check has been run on the port.

API Changes

  • New public field ScanRequest::split_concurrency. Code that builds ScanRequest with ..Default::default() is unaffected.
  • New ScanOptions.splitConcurrency() builder option in JNI, and a new vortex.splitConcurrency Spark read option. Unset keeps today's behaviour.

AI assistance

Disclosed per the AI policy: AI tools helped write and review this change. Claude Code prepared this upstream port, issue and PR description at my request. I am the responsible contributor.

🤖 Generated with Claude Code

ChristopherWirt and others added 2 commits November 3, 2026 10:41
ScanRequest gains split_concurrency: the most splits of each partition
decoded at once, in all. Both layout data sources pass it to
ScanBuilder::with_some_max_splits_in_flight, a cap kept apart from the
per-worker concurrency. JNI takes it as ScanOptions.splitConcurrency and
drives capped Arrow streams directly, so Arrow conversion ahead of Java is
bounded by it too. Spark takes it as the vortex.splitConcurrency read option.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Christopher Wirt <[email protected]>
Preserve broadcast Hadoop configuration and carry split concurrency through both VortexIo construction paths.

Signed-off-by: Christopher Wirt <[email protected]>
@robert3005

Copy link
Copy Markdown
Contributor

I think now you're conflicting with #11198

@codspeed

codspeed Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

2 warnings

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 1917 untouched benchmarks
⏩ 47 skipped benchmarks1


Comparing quantcast:cwirt/upstream-scan-split-concurrency (7f0edbc) with develop (807a0c8)

Open in CodSpeed

Footnotes

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

ChristopherWirt and others added 2 commits November 9, 2026 09:22
Resolve the vortex-jni scan.rs conflict with vortex-data#11198: feed the
split-concurrency lookahead into block_on_stream_prefetch, and drop
scan_iterator, whose only job was to avoid the per-core queue that
the prefetch bridge already removes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Christopher Wirt <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Christopher Wirt <[email protected]>
@ChristopherWirt

Copy link
Copy Markdown
Contributor Author

Merged develop and resolved the conflict with #11198: the split cap now feeds block_on_stream_prefetch, so scan_iterator is gone.

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.

2 participants