NVIDIA / NVIDIA/cudf

[QST] Discussion: Parquet hybrid reader read granularity to page-level I/O

Open
#22,931 0 comments 0 reactions 0 assignees View on GitHub
cuIO libcudf question
Dominant language
C++
Stars
9.8k
Forks
1.1k
Avg merge
3d 6m
Merged PRs (30d)
278

Description

# TL;DR of this question & discussion

Is page-level I/O planned as a future feature for the Parquet hybrid reader?

---

## PystachIO and me

I previously contributed to the standard Parquet reader in libcudf, and I received a lot of valuable help from the RAPIDS team during that process. In particular, I would like to thank Muhammad and Vukasin on the cuIO side for their help and for taking over the merging of my previous story issue, especially since I was not able to spend much time on new research or new code during the past half year ❤️ .

Together with my colleague Nils, we two did the work [PystachIO](https://arxiv.org/abs/2512.02862v3) together. PystachIO is our research distributed multi-GPU query engine running fully on NVMe storage and RDMA networks. In PystachIO, the storage layer is built on top of libcudf's standard Parquet reader. More specifically, we perform RG-level pruning externally, not in libcudf. After the list of RG to read is determined, we issue libcudf Parquet read calls for those RG in a multi-threaded way. The main reason for doing the pruning externally was simplicity. In our experiments, this external pruning step takes around 1% of the query runtime, so it is not a bottleneck. For most queries we observe, more than 95% of the time is spent in the storage layer, rather than on GPU computation or RDMA communication.

I also have [another short work](https://arxiv.org/abs/2602.17335), where we show that large RG sizes are needed to saturate SSD bandwidth stably. However, large RG also introduces a natural opportunity for future optimization: even if an RG is selected, it may still contain many pages that are not necessarily needed by the query.

## The new hybrid reader

During our previous PystachIO submission in November last year, the hybrid reader was still under development, so we could not make use of it at that time. PystachIO uses only the standard Parquet reader in libcudf. After being away from this topic for a while, I am now looking into the new hybrid reader design again.

One thing that stands out to me is the current read granularity. From my understanding, the hybrid reader can use page-level information for pruning, but the actual I/O granularity still appears to be at the ColumnChunk level. This is counterintuitive to me. In CPU-side systems, for example, in Arrow Rust, there has been a lot of effort to reduce I/O amplification by issuing smaller-granularity reads when page-level information is available. I would expect this issue to be even more important on the GPU side, because reading unnecessary data also consumes PCIe bandwidth and GPU memory.

## Concern

If the reader has to **unnecessarily** read the full ColumnChunk into GPU memory, the read amplification can become very large for highly selective workloads or random-access workloads.

For example, if only a small number of pages in a ColumnChunk are needed, reading the entire ColumnChunk still causes unnecessary file I/O in PCIe and then unnecessary GPU memory usage. I understand that the hybrid reader has row masks and data page masks. These are useful for avoiding unnecessary decompression and decoding. However, for the use case I am describing, the main concern is **PCIe I/O**: avoiding the read of unnecessary bytes and read amplification in the first place.

## Desired minimal interface

The minimal interface I would like to discuss is something like `read(filename, column_name, row_group_id, page_ids)`, where `page_ids` are data page ordinals inside the selected column chunk. The expected behavior would be that the actual bytes read from storage are close to the compressed sizes of the requested pages, rather than the size of the full ColumnChunk. Also, such an interface exists in Arrow Rust.

I intentionally describe this as a minimal API. The page-selection logic could remain outside libcudf. This may not be the most complete or elegant design for most users, but it would keep the requested change relatively small: libcudf would read and materialize selected pages.

## Possible design reason

I can also understand why the current design may prefer ColumnChunk-level reads. Small page-level I/O can be inefficient, especially with GDS or other storage paths where many small reads may not provide enough bandwidth. In that case, reading a larger continuous range can be better for throughput.

However, for workloads with strong page locality, random access, or very high selectivity, the current ColumnChunk-level read granularity can still be too expensive. It would be useful to have a path where advanced users can trade larger request overhead for much lower read amplification.

---

I may have misunderstood parts of the current design, so corrections are very welcome. Thanks also for the discussion in advance.

I would also be happy to contribute to this direction, although due to my current commitments, this would probably only be realistic toward the end of this year.

Contributor guide

Open the contributing guide

Research direction

The issue names no files, tests, or entry points. Start by reviewing the hybrid reader design and its current ColumnChunk-level I/O, then determine whether page-level reads fit the design and storage paths described. Done means the project has a decided direction for the proposed page-selection interface, with implementation scope established if the feature is accepted.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
data-engineering
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.