acruise opened a new pull request, #7418:
URL: https://github.com/apache/datafusion-comet/pull/7418

   ## Which issue does this PR close?
   
   Closes #7070.
   
   ## Rationale for this change
   
   `NativeBatchDecoderIterator` read each compressed shuffle block into a 
thread-local direct buffer,
   and `close()` shrank that buffer back to 128 KiB whenever it had grown. 
`CometBlockStoreShuffleReader.read`
   creates one iterator per fetched map output and closes the previous one as 
it moves on, so for every
   block over 128 KiB, which at the default batch size is most of them (#5678 
measured 700 KiB to 1.4 MiB),
   a reducer paid two `allocateDirect` calls: one to shrink on close and one to 
grow again on the next
   block. Each is zero-filled and goes through the JDK's direct-memory 
reservation, which can force a
   `System.gc()` near `MaxDirectMemorySize`. The coalescing read reset the 
buffer at every block boundary
   through a flag on `concatenate`, for the same effect. And the reset ran on 
whichever thread called
   `close()`, so a close from the task-completion thread replaced that thread's 
buffer, not the reader's.
   
   The direct-read path, `CometShuffleBlockIterator`, already keeps one buffer 
per task that grows and
   stays. This brings the JVM-consumer path, used by stages that end in a Spark 
operator and by reads
   without AQE, in line with it.
   
   ## What changes are included in this PR?
   
   - `ShuffleReadBuffer` (new): a growable direct buffer owned by a task. 
`prepare(bytes)` returns it
     cleared with its limit at `bytes`, allocating 128 KiB for the first block 
or twice the block when
     one outgrows it; `release()` drops the reference. Releasing never frees 
the memory itself: the task
     thread fills the buffer outside the iterator's monitor, so that completion 
can close the input it
     reads from, and a read still in flight keeps its own reference until the 
cleaner runs.
   - `NativeBatchDecoderIterator` takes a `readBuffer` (default: its own), 
reads each block through
     `prepare`, and no longer resets anything in `close()`. The thread-local 
and `resetDataBuf` are gone.
   - `CometBlockStoreShuffleReader.read` creates one `ShuffleReadBuffer`, 
passes it to every iterator,
     and releases it in the completion listener after closing the last iterator 
and the `NativeUtil`.
     `readWithCoalescing` does the same for its single iterator. 
`readAsRawStream` and `concatenate` lose
     the reset flag.
   - `CometCelebornShuffleReader` is unchanged: it creates one iterator per 
task, whose own buffer is
     therefore already per task.
   - `fetchIterator` becomes `protected[shuffle]` so a test can hand the reader 
its blocks.
   
   ## How are these changes tested?
   
   - `CometBlockStoreShuffleReaderSuite`: `ShuffleReadBuffer` grows to twice 
the block, keeps its size and
     position/limit contract across smaller blocks, and lets go on release.
   - `NativeBatchDecoderIteratorSuite`:
     - `coalescing keeps the task's read buffer across fetched blocks` (was 
`... releases oversized read
       buffers between fetched blocks`): three frames across two fetched 
blocks, the first over 128 KiB,
       arrive in the same buffer object.
     - `a reduce task reads every fetched block into one buffer`: 
`CometBlockStoreShuffleReader.read` over
       three fetched blocks, two of them over 128 KiB, allocates exactly one 
direct buffer. It reports
       three when the reader does not pass its buffer to the iterators.
   - `CometCelebornShuffleReaderSuite`, which hosts the decoder lifecycle and 
concurrency checks, passes
     unchanged (78 tests across the three suites).
   
   Throwaway harness, not included: `CometBlockStoreShuffleReader.read` over 
400 fetched blocks of 169 KiB
   per task (uncompressed frames of 40K ints, real native decode), best of 15 
rounds after 5 warmup, three
   JVMs each, Apple M3 Max, JDK 21:
   
   | | per block (best) | per task, median |
   |---|---|---|
   | main | 53 to 58 us | 28 to 31 ms |
   | this PR | 23 to 25 us | 10 to 11 ms |
   
   The difference is the two zero-filled 340 KiB allocations per block. Not 
measured on a cluster, where
   the direct-memory reservation's `System.gc()` fallback would add to it.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to