[SPARK-56438][SQL][CORE] Optimize VectorizedPlainValuesReader.readBinary for direct ByteBuffer by eliminating intermediate byte[] copy#55296
Open
LuciferYang wants to merge 4 commits intoapache:masterfrom
Conversation
LuciferYang
commented
Apr 10, 2026
| * Returns the native memory address of a direct {@link ByteBuffer}. | ||
| * The buffer must be direct; passing a heap buffer produces an undefined result. | ||
| */ | ||
| public static long getDirectBufferAddress(ByteBuffer buffer) { |
Contributor
Author
There was a problem hiding this comment.
If this pr is accepted, a corresponding benchmark can be added to PlatformBenchmark at a later time.
Member
There was a problem hiding this comment.
+1. It'd be great to have some benchmark results!
sunchao
reviewed
Apr 11, 2026
Member
sunchao
left a comment
There was a problem hiding this comment.
Looks reasonable to me. Left some comments.
| Platform.copyMemory(src.array(), Platform.BYTE_ARRAY_OFFSET + src.arrayOffset() + srcIndex, | ||
| null, data + rowId, count); | ||
| } else { | ||
| long srcAddr = Platform.getDirectBufferAddress(src) + srcIndex; |
Member
There was a problem hiding this comment.
I think hasArray() does not necessarily mean the buffer is a direct buffer. We can perhaps strengthen this by also check src.isDirect?
| * Copies {@code count} bytes from a {@link ByteBuffer} starting at absolute position | ||
| * {@code srcIndex} into this column at {@code rowId}. Does not modify the buffer's position. | ||
| */ | ||
| public abstract void putBytes(int rowId, int count, ByteBuffer src, int srcIndex); |
Member
There was a problem hiding this comment.
Anyway we can add some test cases for this new API?
LuciferYang
commented
Apr 13, 2026
| verifyPutByteArray(testVector) | ||
| } | ||
|
|
||
| testVectors("putBytes from ByteBuffer", 16, ByteType) { testVector => |
Contributor
Author
There was a problem hiding this comment.
@sunchao Added new tests for both new APIs.
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.
What changes were proposed in this pull request?
This PR optimizes
VectorizedPlainValuesReader.readBinaryfor the direct (non-heap)ByteBufferpath by eliminating an intermediatebyte[]copy.Previously, when reading binary/string values from a direct
ByteBuffer, each value required:byte[len]ByteBufferinto the temp arrayThis PR adds
ByteBuffer-awareputBytes/putByteArrayoverloads toWritableColumnVector, enabling a single-copy path:OnHeapColumnVector: usesByteBuffer.get(index, byte[], offset, length)(absolute bulk get) to copy directly into the backingbyte[]— one native-to-heap copy.OffHeapColumnVector: usesPlatform.copyMemorywith the direct buffer's native address — one native-to-native copy.A
Platform.getDirectBufferAddress(ByteBuffer)helper is added to read aDirectByteBuffer's native address via theBuffer.addressfield offset, consistent withPlatform's existingUnsafe-based accessor pattern.Why are the changes needed?
In Spark's vectorized Parquet reader, binary/string columns from memory-mapped (direct)
ByteBuffersources incur two fullmemcpyoperations per value. The intermediatebyte[]allocation also adds GC pressure.Eliminating one copy per value yields a 10–22% improvement on the default
DIRECT/ON_HEAPpath across JDK 17, 21, and 25.Does this PR introduce any user-facing change?
No.
How was this patch tested?
Benchmark Code (click to expand)
Perform
build/sbt "sql/Test/runMain org.apache.spark.sql.execution.datasources.parquet.VectorizedPlainValuesReaderBenchmark"to conduct the testBenchmark results(click to expand)
Across JDK 17, 21 and 25, the default
DIRECT/ON_HEAPpath achieves a performance improvement of 10%–22% andHEAPpath results are unchanged as expected (code path not affected).Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code