perf(core): zero-copy block receive on the remote fetch path - #2312
Merged
Conversation
…taStream `BlockDataStream` concatenated every incoming block into a freshly allocated buffer, even when nothing was pending. `Buffer::from(Bytes)` adopts the transport allocation rather than copying it, and `StreamDecoder::decode` drains `state_buffer` completely before the stream asks for another block, so in the steady state the block can be taken as-is. Replace `combine_buffers` with `append_block`, which adopts the incoming block when nothing is pending and falls back to concatenating only for a partial message straddling a block boundary — the case the schema accumulation loop in `try_new` relies on. 65 MiB payload at the server's 8 MiB block size: 69.7 ms -> 22.7 ms (-67%). At 1 MiB blocks: 41.8 ms -> 34.9 ms (-17%). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
milenkovicm
approved these changes
Aug 16, 2026
milenkovicm
left a comment
Contributor
There was a problem hiding this comment.
Thanks @Dandandan makes sense
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?
N/A
Rationale for this change
BlockDataStreamcopied every incoming transport block into a freshly allocated buffer, even when nothing was pending:Two facts make that copy avoidable in the steady state:
Buffer::from(bytes::Bytes)adopts the transport allocation rather than copying it.StreamDecoder::decodeloopswhile !buffer.is_empty(), and every non-error early return isOk(Some(batch)). So whenever it returnsOk(None)— the only condition under whichpoll_nextpulls another block —state_bufferhas been fully drained.So on the hot path the incoming block can simply be adopted. Concatenation is only needed for a partial message straddling a block boundary, which is what the schema-accumulation loop in
try_newrelies on (try_schema_from_ipc_bufferpeeks without consuming, so a partial header stays pending there).What changes are included in this PR?
combine_buffersis replaced byappend_block, which adopts the incoming block when nothing is pending and otherwise falls back to the existing concatenating path. Both call sites — the schema loop intry_newandextend_bytes— move to it.This is on by default for all remote shuffle reads; there is no new configuration.
Are these changes tested?
Yes. The existing
BlockDataStreamtests cover both paths and still pass —should_process_chunked(2-byte blocks) drives the concatenating path in the schema loop, andshould_process_single_messagecovers whole-block adoption.Added
should_process_multi_block_payload, which round-trips a payload spanning several whole blocks at 8/64/512-byte block sizes, covering the mixed regime this change targets.cargo test --package ballista-core --lib client::— 6 passed, 0 failed.cargo clippy --all-targets --package ballista-core --all-features -- -D warningsandcargo fmt --all -- --checkare clean.Benchmark
65 MiB payload at the server's 8 MiB block size: 69.7 ms → 22.7 ms (−67%). At 1 MiB blocks: 41.8 ms → 34.9 ms (−17%).
Are there any user-facing changes?
No API changes —
combine_buffersandappend_blockare both private. Remote shuffle reads get faster.🤖 Generated with Claude Code