Skip to content

perf(scan): announce every chunk's reads when a V2 scan starts - #10847

Draft
joseph-isaacs wants to merge 1 commit into
ji/segment-iofrom
ji/segment-io-announce
Draft

joseph-isaacs wants to merge 1 commit into
ji/segment-iofrom
ji/segment-io-announce

Conversation

@joseph-isaacs

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

Copy link
Copy Markdown
Contributor

Summary

Stacked on #10527. Ports the cross-split early announcements from the earlier segment IO branch onto develop's planner-chain V2 executor.

Each chunk of a V2 scan runs on a driver of its own, so per-chunk announcements only let a chunk's reads coalesce with that chunk's own segments. On TPC-H SF1 that left #10527 at 2199 physical reads, against 882 for v1.

Changes

  • On its first compute, ChunkPlanner announces the filter's and the projection's segments for every chunk still to plan.
    • It lists them through the same selected_rows that plan_selection uses, so pruned and unselected rows are skipped.
  • PreparedScan::announce submits IoIntent::Announce requests through the scan's IO service.
    • The scan keeps the handles for its lifetime; every planner holds the Arc<PreparedScan>.
    • Announcements read nothing themselves; they only let the IO service widen real reads.
  • VORTEX_SCAN_EARLY_ANNOUNCE=0 falls back to per-chunk announcements, for A/B comparisons. This knob could be dropped before merge.
  • New test: a_scan_announces_every_chunk_before_planning_one.

Known trade-off: the projection segments of chunks that the filter later empties are also announced. Coalescing may therefore read some bytes that are never used.

Local TPC-H SF1, DataFusion + Vortex, NVMe (develop a59294e)

physical reads total ms (median of 15) geomean vs develop V2
v1 882 1522 1.006
V2 develop 2099 1536 1.000
V2 #10527 (VORTEX_SCAN_EARLY_ANNOUNCE=0) 2199 1596 1.055
V2 this PR 447 1502 0.995

Physical reads = io.requests.coalesced + io.requests.individual.

CI pr-bench-sql.yml (dispatched; one run each)

Geomean of per-query time against the develop base ji/bench-base-a59294e. DataFusion on Parquet and DuckDB on Vortex (V1 scan) are controls, since neither PR touches them. Each control cell gives DuckDB:vortex / DF:parquet.

benchmark #10527 DF:vortex #10527 controls this PR DF:vortex this PR controls
TPC-H SF1 NVMe 1.017 1.014 / 1.000 1.009 1.009 / 1.000
TPC-H SF10 NVMe 1.019 1.049 / 0.999 0.999 1.016 / 0.998
TPC-H SF1 S3 1.083 1.156 / 0.977 1.029 1.048 / 1.017

In CI, wall time does not move beyond the noise shown by the controls; on S3 the untouched DuckDB:vortex control moves by up to 16%. The cut in physical reads does not yet turn into time on these runners.

#10527's S3 result comes from a bot rerun (attempt 2) after a spot interruption. The dispatched runs report as failed only because their "Comment PR" step needs a PR context. The benchmark and compare steps succeed.

Checks

  • cargo nextest run -p vortex-layout: pass, including the new test.
  • cargo clippy -p vortex-layout --all-targets --all-features -- -D warnings: clean.
  • cargo +nightly-2026-09-10 fmt --check: clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N4PmceDHg8rFe5bgkhGFU9

Each chunk of a V2 scan runs on a driver of its own, so a chunk's announcements
only let its reads coalesce with its own segments. The chunk planner now
announces the filter's and the projection's segments over every chunk still to
plan on its first compute, and the prepared scan holds those announcements for
its lifetime, so the IO service can widen each read with the segments around it.

On TPC-H SF1 with DataFusion over local files this cuts physical reads from
2199 to 447 (v1: 882). Setting VORTEX_SCAN_EARLY_ANNOUNCE=0 restores per-chunk
announcements, for comparisons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4PmceDHg8rFe5bgkhGFU9
Signed-off-by: Claude <noreply@anthropic.com>
@codspeed

codspeed Bot commented Oct 27, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 12.11%

3 warnings, 1 note

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

⚠️ Unable to measure the execution of 2 benchmarks

These benchmarks measured no execution time, so their results are not comparable and count as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

⚠️ 25 benchmarks are 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

ℹ️ 40 benchmarks have no flamegraph

The flamegraph wasn't generated: the symbols of the profiling trace could not be resolved, because no information about the language frames was collected.

See affected benchmarks

⚡ 1 improved benchmark
✅ 2438 untouched benchmarks
⏩ 12 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
⚡ Walltime starts_with_avx512[64, VarBin] 62.1 µs 55.4 µs +12.11%

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/segment-io-announce (c3d0d12) with ji/segment-io (054036d)

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

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants