Skip to content

perf(bigquery): [T1-12] replace Channels.newChannel bounce buffer with direct ByteBuffer channel on Arrow batches - #27

Open
lqiu96 wants to merge 1 commit into
mainfrom
perf/bq-t1-12-arrow-bytebuffer-channel
Open

lqiu96 wants to merge 1 commit into
mainfrom
perf/bq-t1-12-arrow-bytebuffer-channel

Conversation

@lqiu96

@lqiu96 lqiu96 commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Summary & Bottleneck

  • Target files:
    • java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/ArrowDeserializer.java (L165-L169)
    • java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/ArrowQueryResultImpl.java (L528-L530)
    • java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/ConnectionImpl.java (L1115-L1118, L1139-L1142)
  • Scaling cardinality: $O(\text{batches})$ allocations and double-copies across all Arrow IPC record batches.
  • Bottleneck details:
    1. ArrowDeserializer.loadArrowRows and ArrowQueryResultImpl.VectorBatchIterator.loadBatch(ByteString) wrapped batch.getSerializedRecordBatch().newInput() in Channels.newChannel(...). In JDK's Channels.ReadableByteChannelImpl, read(ByteBuffer dst) allocates a byte[8192] transfer buffer, acquires synchronized (readLock) on every read, copies bytes from InputStream into byte[8192], and then copies from byte[8192] into dst.
    2. ConnectionImpl.ArrowRowReader called batch.getSerializedRecordBatch().toByteArray() and arrowSchema.getSerializedSchema().toByteArray(), allocating a full byte[] copy of every serialized Arrow schema and record batch payload before wrapping it in ByteArrayReadableSeekableByteChannel.

Changes

  • Direct ByteBuffer channel over ByteString.asReadOnlyByteBuffer():
    • Added ArrowDeserializer.newByteStringChannel(ByteString) returning a lightweight, unsynchronized ByteBufferReadableByteChannel that copies slices directly from byteString.asReadOnlyByteBuffer() into the target ByteBuffer.
  • Replaced Channels.newChannel and toByteArray() across Arrow readers:
    • Updated ArrowDeserializer.loadArrowRows, ArrowQueryResultImpl.VectorBatchIterator.loadBatch(ByteString), and ConnectionImpl.ArrowRowReader to use ArrowDeserializer.newByteStringChannel(...).

Expected Impact

  • Eliminates the byte[8192] bounce buffer allocation, double memory copy, and synchronized (readLock) overhead per Arrow record batch in ArrowDeserializer and ArrowQueryResultImpl.
  • Eliminates full-batch byte[] array copies (toByteArray()) per Arrow record batch in ConnectionImpl.ArrowRowReader.

Verification

  • Ran mvn test -pl java-bigquery/google-cloud-bigquery -Dtest=ArrowDeserializerTest,ArrowQueryResultTest,ArrowQueryPageFetcherTest,ConnectionImplTest (57 tests run, 0 failures).
  • Added ArrowDeserializerTest.testNewByteStringChannelReadsIncrementalSlicesAndCloses to verify incremental slice reads, EOF semantics, and channel closure behavior.

…h direct ByteBuffer channel on Arrow batches
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.

1 participant