iemejia commented on code in PR #55932:
URL: https://github.com/apache/spark/pull/55932#discussion_r3752926239


##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -113,11 +119,12 @@ public ByteBuffer getBytes(int rowId) {
 
   @Override
   public void skipBinary(int total) {
+    long totalSkip = 0;
     for (int i = 0; i < total; i++) {
-      int remaining = lengthsVector.getInt(currentRow + i);
-      while (remaining > 0) {
-        remaining -= in.skip(remaining);
-      }
+      totalSkip += lengthsVector.getInt(currentRow + i);
+    }
+    while (totalSkip > 0) {
+      totalSkip -= in.skip(totalSkip);

Review Comment:
   Good catch, fixed. `skipBinary` now uses `in.skipFully(totalSkip)` and wraps 
the resulting `IOException` in a `ParquetDecodingException`, so an exhausted 
stream fails fast with a diagnostic instead of spinning (and remains 
interruptible). Each length is validated non-negative before being summed, so 
`totalSkip` can never be negative and trigger a rewind. Added a test 
(`skipBinary fails cleanly when the data region is truncated`) that decodes a 
full length header over a truncated data region and asserts the exception.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -54,17 +54,26 @@ public void initFromPage(int valueCount, 
ByteBufferInputStream in) throws IOExce
 
   @Override
   public void readBinary(int total, WritableColumnVector c, int rowId) {
-    ByteBuffer buffer;
-    ByteBufferOutputWriter outputWriter = 
ByteBufferOutputWriter::writeArrayByteBuffer;
-    int length;
+    // Compute total data length across all values so we can read everything 
in a single
+    // bulk slice instead of allocating one ByteBuffer per value.
+    int totalDataLen = 0;
     for (int i = 0; i < total; i++) {
-      length = lengthsVector.getInt(currentRow + i);
-      try {
-        buffer = in.slice(length);
-      } catch (EOFException e) {
-        throw new ParquetDecodingException("Failed to read " + length + " 
bytes");
-      }
-      outputWriter.write(c, rowId + i, buffer, length);
+      totalDataLen += lengthsVector.getInt(currentRow + i);

Review Comment:
   Addressed by reverting `readBinary` to the per-value `in.slice(length)` 
implementation, as suggested in your summary comment below. There is no longer 
any `int` summation of file-supplied lengths in `readBinary`, so the 
overflow-to-zero path is gone. The only remaining summation is in `skipBinary`, 
which now accumulates into a `long`.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -113,11 +119,12 @@ public ByteBuffer getBytes(int rowId) {
 
   @Override
   public void skipBinary(int total) {
+    long totalSkip = 0;
     for (int i = 0; i < total; i++) {
-      int remaining = lengthsVector.getInt(currentRow + i);
-      while (remaining > 0) {
-        remaining -= in.skip(remaining);
-      }
+      totalSkip += lengthsVector.getInt(currentRow + i);

Review Comment:
   Fixed in `skipBinary`: each length is validated `>= 0` before being added to 
the running total, so a batch like `[100, -100]` now throws 
`ParquetDecodingException` rather than summing to 0 and letting `currentRow` 
drift. The `readBinary` side of this concern is resolved by reverting that 
method to `master` (no batch summation there).
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -54,17 +54,26 @@ public void initFromPage(int valueCount, 
ByteBufferInputStream in) throws IOExce
 
   @Override
   public void readBinary(int total, WritableColumnVector c, int rowId) {
-    ByteBuffer buffer;
-    ByteBufferOutputWriter outputWriter = 
ByteBufferOutputWriter::writeArrayByteBuffer;
-    int length;
+    // Compute total data length across all values so we can read everything 
in a single

Review Comment:
   Agreed, and done. The `readBinary` bulk-slice change has been dropped 
entirely and reverted (along with `readGeoData`) to `master`. The PR now 
contains only the measurable `skipBinary` win plus the validation fixes. The 
description and the umbrella issue have been updated to drop the `readBinary` 
table; a fresh AMD EPYC 7763 benchmark run on the reverted code confirms 
`readBinary` now measures identical to the baseline (a true no-op), while 
`skipBinary` stays at a consistent 2.00x-2.33x.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -54,17 +54,26 @@ public void initFromPage(int valueCount, 
ByteBufferInputStream in) throws IOExce
 
   @Override
   public void readBinary(int total, WritableColumnVector c, int rowId) {
-    ByteBuffer buffer;
-    ByteBufferOutputWriter outputWriter = 
ByteBufferOutputWriter::writeArrayByteBuffer;
-    int length;
+    // Compute total data length across all values so we can read everything 
in a single
+    // bulk slice instead of allocating one ByteBuffer per value.
+    int totalDataLen = 0;
     for (int i = 0; i < total; i++) {
-      length = lengthsVector.getInt(currentRow + i);
-      try {
-        buffer = in.slice(length);
-      } catch (EOFException e) {
-        throw new ParquetDecodingException("Failed to read " + length + " 
bytes");
-      }
-      outputWriter.write(c, rowId + i, buffer, length);
+      totalDataLen += lengthsVector.getInt(currentRow + i);
+    }
+
+    ByteBuffer allData;
+    try {
+      allData = in.slice(totalDataLen);
+    } catch (EOFException e) {
+      throw new ParquetDecodingException("Failed to read " + totalDataLen + " 
bytes");
+    }
+    byte[] dataArray = allData.array();

Review Comment:
   Resolved by reverting `readBinary` - the unguarded `array()` access is gone 
with the bulk-slice removal.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -54,17 +54,26 @@ public void initFromPage(int valueCount, 
ByteBufferInputStream in) throws IOExce
 
   @Override
   public void readBinary(int total, WritableColumnVector c, int rowId) {
-    ByteBuffer buffer;
-    ByteBufferOutputWriter outputWriter = 
ByteBufferOutputWriter::writeArrayByteBuffer;
-    int length;
+    // Compute total data length across all values so we can read everything 
in a single
+    // bulk slice instead of allocating one ByteBuffer per value.
+    int totalDataLen = 0;
     for (int i = 0; i < total; i++) {
-      length = lengthsVector.getInt(currentRow + i);
-      try {
-        buffer = in.slice(length);
-      } catch (EOFException e) {
-        throw new ParquetDecodingException("Failed to read " + length + " 
bytes");
-      }
-      outputWriter.write(c, rowId + i, buffer, length);
+      totalDataLen += lengthsVector.getInt(currentRow + i);
+    }
+
+    ByteBuffer allData;
+    try {
+      allData = in.slice(totalDataLen);

Review Comment:
   Resolved by the revert - `readBinary` no longer takes a whole-batch slice, 
so the `MultiBufferInputStream.slice` allocate+memcpy path is no longer on the 
hot path.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -85,19 +94,16 @@ public void readGeography(int total, WritableColumnVector 
c, int rowId) {
 
   private void readGeoData(int total, WritableColumnVector c, int rowId, int 
srid,
      WKBConverterStrategy converter) {
-    ByteBufferOutputWriter outputWriter = 
ByteBufferOutputWriter::writeArrayByteBuffer;
-    int length;
     for (int i = 0; i < total; i++) {
-      length = lengthsVector.getInt(currentRow + i);
+      int length = lengthsVector.getInt(currentRow + i);
       byte[] physicalValue;
       try {
         // Converts WKB into a physical representation of geometry/geography.
         physicalValue = converter.convert(in.readNBytes(length), srid);
       } catch (IOException e) {
         throw new ParquetDecodingException("Failed to read " + length + " 
bytes");
       }
-
-      outputWriter.write(c, rowId + i, ByteBuffer.wrap(physicalValue), 
physicalValue.length);
+      c.putByteArray(rowId + i, physicalValue, 0, physicalValue.length);

Review Comment:
   Since we reverted `readGeoData` as well, both 
`ByteBufferOutputWriter::writeArrayByteBuffer` call sites remain, so the 
interface still has callers and this PR no longer makes it dead code. Removing 
it is therefore out of scope here - happy to file a separate follow-up cleanup 
for it if you'd like.
   



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/VectorizedDeltaLengthByteArrayReader.java:
##########
@@ -113,11 +119,12 @@ public ByteBuffer getBytes(int rowId) {
 
   @Override
   public void skipBinary(int total) {
+    long totalSkip = 0;

Review Comment:
   Added `skipBinary fails cleanly when the data region is truncated`: it 
writes a valid page, truncates the data region so the decoded lengths declare 
more bytes than the stream holds, and asserts `skipBinary` throws 
`ParquetDecodingException`. This directly exercises the dry-stream case (the 
infinite-loop fix). The overflow case no longer applies now that `readBinary` 
is reverted and `skipBinary` sums into a `long`; a negative-length page can't 
be produced through the parquet writer, but that path is guarded by the 
explicit `length < 0` check.
   



-- 
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