Skip to content

Use configured copy buffer for Hadoop vectored reads [databricks] - #15242

Merged
binmahone merged 1 commit into
NVIDIA:mainfrom
binmahone:fix-readvectored-hadoop-fallback-main
Jul 13, 2026
Merged

Use configured copy buffer for Hadoop vectored reads [databricks]#15242
binmahone merged 1 commit into
NVIDIA:mainfrom
binmahone:fix-readvectored-hadoop-fallback-main

Conversation

@binmahone

Copy link
Copy Markdown
Collaborator

Fixes #15163.

Description

This is the main forward-port of #15164, which targeted release/26.06.

When an optimized input-file implementation is not selected, remote Parquet reads use HadoopInputFile and the generic RapidsInputFile.readVectored fallback. Before NVIDIA/cudf-spark-jni#4765, that fallback used HostMemoryBuffer.copyFromStream with a 128 KiB internal copy chunk, while the pre-#14674 Hadoop copy loop used parquet.read.allocation.size with an 8 MiB default.

This PR overrides HadoopInputFile.readVectored to use the caller-supplied-buffer helper from the JNI main forward-port, NVIDIA/cudf-spark-jni#4808:

  • read the copy size from parquet.read.allocation.size
  • default to the JNI fallback size of 8 MiB
  • allocate a temporary buffer for each readVectored call
  • avoid retained shared buffers and ThreadLocal state
  • leave GpuParquetScan on the inputFile.readVectored abstraction introduced by optimize iceberg read #14674

The measured regression was on the S3A fallback with PerfIO disabled. The same HadoopInputFile fallback is also used by GCS and other Hadoop-backed filesystems that do not provide their own optimized readVectored implementation. When S3 PerfIO is enabled, S3InputFile.readVectored continues to use the optimized PerfIO path and bypasses this fallback.

Requires NVIDIA/cudf-spark-jni#4808. The previous release/26.06 implementation is #15164 and NVIDIA/cudf-spark-jni#4765.

Testing

The JNI main forward-port (daca8177f626c4c48676beea5f16d36a974620fe) passed all eight RapidsInputFileTest tests. After installing that JNI 26.08.0-SNAPSHOT artifact locally, this commit (5ec2c7ffc1763d5b141e20066ab303e9c287e962) was packaged on an x86 host with:

mvn -B -pl sql-plugin -am -DskipTests \
  -Dspark-rapids-jni.version=26.08.0-SNAPSHOT package

Result: BUILD SUCCESS for all four reactor modules.

Five interleaved full-NDS runs per configuration did not reproduce a stable performance regression after the fix, with either PerfIO disabled or enabled. See #15163 for the complete environment, per-run results, and analysis.

Checklists

Documentation

  • Updated for new or modified user-facing features or behaviors
  • No user-facing change

Testing

  • Added or modified tests to cover new code paths
  • Covered by existing tests
  • Not required

Performance

  • Tests ran and results are added in the PR description
  • Issue filed with a link in the PR description
  • Not required

Forward-port NVIDIA#15164 to main.

Signed-off-by: Hongbin Ma <mahongbin@apache.org>
@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a Hadoop-specific copy-buffer path for fallback vectored reads. The main changes are:

  • Reads parquet.read.allocation.size when creating HadoopInputFile.
  • Stores a validated per-file copy buffer size.
  • Overrides readVectored to allocate a temporary byte array and call the JNI helper.

Confidence Score: 5/5

This looks safe to merge after a small hardening cleanup.

  • The changed read path keeps the temporary buffer local to each call.
  • Oversized parquet.read.allocation.size values can still trigger large executor heap allocations.
  • No blocking correctness or security issue was found in the changed code.

sql-plugin/src/main/java/com/nvidia/spark/rapids/fileio/hadoop/HadoopInputFile.java

Important Files Changed

Filename Overview
sql-plugin/src/main/java/com/nvidia/spark/rapids/fileio/hadoop/HadoopInputFile.java Adds configurable buffering for Hadoop fallback vectored reads, with one hardening issue around oversized buffer settings.

Reviews (1): Last reviewed commit: "Use configured copy buffer for Hadoop ve..." | Re-trigger Greptile

if (copyRanges.isEmpty()) {
return;
}
byte[] copyBuffer = new byte[copyBufferSize];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unbounded Copy Buffer Allocation

copyBufferSize comes directly from the Hadoop parquet.read.allocation.size setting and is allocated on every vectored read. If that setting is accidentally set very large, each fallback Parquet read can allocate a huge on-heap byte array and fail the executor with an allocation error before any data is copied.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Hadoop-backed RapidsInputFile fallback for vectored reads so it uses a configurable copy buffer size (via parquet.read.allocation.size) rather than relying on a small fixed internal copy chunk, addressing the remote Parquet scan performance regression described in #15163.

Changes:

  • Add a HadoopInputFile.readVectored(...) override that uses RapidsInputFile.readVectoredUsingCopyBuffer(...) with a per-call temporary byte buffer.
  • Read the copy buffer size from Hadoop Configuration (parquet.read.allocation.size), defaulting to RapidsInputFile.DEFAULT_READ_VECTORED_COPY_BUFFER_SIZE, and validate it is positive.

binmahone added a commit to NVIDIA/cudf-spark-jni that referenced this pull request Jul 9, 2026
## Description

This is the `main` forward-port of #4765, which
targeted `release/26.06`.

It updates the default `RapidsInputFile.readVectored` fallback to copy
through an explicit byte-buffer loop and write into the destination
`HostMemoryBuffer` with `setBytes`:

- allocate a bounded per-call buffer using the smaller of 8 MiB and the
largest requested range
- expose `RapidsInputFile.readVectoredUsingCopyBuffer(..., byte[])` for
callers with a well-defined scratch-buffer lifecycle
- keep validation and stream-copy details in a package-private utility
- avoid retained shared buffers and `ThreadLocal` state

Related issue: NVIDIA/cudf-spark#15163.

The corresponding cudf-spark `main` forward-port,
NVIDIA/cudf-spark#15242, uses the caller-supplied-buffer helper in
`HadoopInputFile.readVectored`, allowing Hadoop-backed fallback reads to
honor `parquet.read.allocation.size` while preserving the
`inputFile.readVectored` abstraction.

## Testing

The current `main` forward-port commit
(`daca8177f626c4c48676beea5f16d36a974620fe`) was tested on an x86 host
with:

```text
mvn -B -Dmaven.antrun.skip=true -Dtest=RapidsInputFileTest \
  test-compile surefire:test@default-test
```

Result:

```text
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
```

The JNI `26.08.0-SNAPSHOT` artifact was then installed locally and used
to package the corresponding cudf-spark `main` forward-port:

```text
mvn -B -pl sql-plugin -am -DskipTests \
  -Dspark-rapids-jni.version=26.08.0-SNAPSHOT package
```

Result: `BUILD SUCCESS` for all four reactor modules.

## Performance Validation

End-to-end NDS validation of the same implementation is documented in
NVIDIA/cudf-spark#15163. Five interleaved runs per side did not
reproduce a stable regression with either PerfIO disabled or enabled.

Signed-off-by: Hongbin Ma <mahongbin@apache.org>
@binmahone binmahone changed the title Use configured copy buffer for Hadoop vectored reads Use configured copy buffer for Hadoop vectored reads [databricks] Jul 9, 2026
@sameerz sameerz added the performance A performance related task/issue label Jul 9, 2026
@binmahone

Copy link
Copy Markdown
Collaborator Author

build

@gerashegalov
gerashegalov self-requested a review July 10, 2026 23:16
return new HadoopInputStream(fs.open(filePath));
}

@Override

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit [non-blocking]: The override silently drops the null-guards that the interface default provides. A null copyRanges will NPE on .isEmpty() without a helpful message, and a null output will fail deep inside RapidsInputFileUtils. Consider adding the same two guards from the default:

Objects.requireNonNull(output, "output can't be null");
Objects.requireNonNull(copyRanges, "copyRanges can't be null");

@gerashegalov gerashegalov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@binmahone

Copy link
Copy Markdown
Collaborator Author

build

@binmahone
binmahone merged commit e301446 into NVIDIA:main Jul 13, 2026
56 of 98 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance A performance related task/issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parquet Hadoop fallback readVectored uses 128 KiB copies and can regress remote scans

6 participants