perf: buffer the NestedLoopJoin build side as coalesced chunks instead of one concat_batches allocation - #24820
Open
ranflarion wants to merge 1 commit into
Open
perf: buffer the NestedLoopJoin build side as coalesced chunks instead of one concat_batches allocation#24820ranflarion wants to merge 1 commit into
ranflarion wants to merge 1 commit into
Conversation
…ne concat_batches allocation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #24820 +/- ##
==========================================
- Coverage 81.58% 81.58% -0.01%
==========================================
Files 1123 1123
Lines 406610 406743 +133
Branches 406610 406743 +133
==========================================
+ Hits 331720 331826 +106
- Misses 55452 55468 +16
- Partials 19438 19449 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Rationale for this change
NestedLoopJoinExecbuffered the whole build side into one batch viaconcat_batches, which doubles peak memory while the copy runs (inputs and output coexist), and the concat output is never reserved in the memory pool, so the doubled peak is invisible to it. On an ~880 MB build side (repro in #24819, runnable with stockdatafusion-cli), main peaks at 1738 MB RSS; with--memory-limit 1git completes while peaking at 1739 MB, 1.7x its own limit. A single allocation also caps any one string column ati32::MAXbytes (the overflow reported in #23032) and can only be spilled or released wholesale.This PR keeps the build side as target-batch-size chunks instead. Same repro after the change: 899 MB peak (1.02x the build side), 900 MB under
-m 1g. Wall time on the repro improves ~20% since the concat copy is gone.What changes are included in this PR?
JoinLeftDataholdsVec<RecordBatch>chunks with prefix-sumrow_offsets, a binary-searchlocate(global_row) -> (chunk, local_row), and the build schema (so an empty build side keeps its shape). Zero-row chunks are dropped at construction.collect_left_inputfeeds arrow'sBatchCoalescerwithwith_biggest_coalesce_batch_size(target/2): small batches are compacted to target size, batches at or above half the target pass through zero-copy. Reservation still charges each input batch'sget_array_memory_sizeas before.concat_batchesin the memory-limited rebuild is gone too.take/slice use chunk-local indices, and the visited-left bitmap keeps global row numbers, so bitmap semantics (including the multi-partition rules from fix: refuse memory-limited NestedLoopJoin fallback for left-emission joins with a multi-partition probe side #24675 and the deferred emission from fix: emit deferred unmatched rows when memory-limited NestedLoopJoin exhausts its left side #24746) are unchanged. The outputBatchCoalescerre-coalesces the occasionally smaller batch emitted at a chunk tail.One behavior note: a sliced batch at or above half the target size is now retained as-is, keeping its parent allocation alive, where the old concat incidentally un-pinned it by copying. The reservation charges the full parent buffers, so the pool over-counts rather than under-counts in that case; slice-aware accounting (dedup by allocation) is a planned follow-up.
Are these changes tested?
Existing coverage: the full NLJ suite including the memory-limited matrix and the one-shot re-execution test,
physical-planlib tests, the join fuzz suite (--features extended_tests), thememory_limitintegration tests, and the join sqllogictests all pass. New unit tests: chunks retain the input buffers by pointer identity (the zero-copy bypass), and zero-row chunks are dropped withlocate()boundary checks.Probe-throughput parity, medians over interleaved runs of ~1.6e10 pair evaluations, release builds, same machine:
Are there any user-facing changes?
No. Plans, results, and metrics are unchanged; only the build side's in-memory layout and its peak memory differ.