Skip to content

[PERF]: Improve the performance of plc.io.parquet.read_parquet with prefetched parquet file metadata. - #23558

Open
TomAugspurger wants to merge 31 commits into
NVIDIA:mainfrom
TomAugspurger:tom/parquet-metadata-gil-perf
Open

[PERF]: Improve the performance of plc.io.parquet.read_parquet with prefetched parquet file metadata.#23558
TomAugspurger wants to merge 31 commits into
NVIDIA:mainfrom
TomAugspurger:tom/parquet-metadata-gil-perf

Conversation

@TomAugspurger

@TomAugspurger TomAugspurger commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

We've observed a performance penalty from enabling metadata prefetching in cudf-polars for data in local storage. We'd like to enable prefetching by default, since it's crucial for good performance with high-latency remote storage systems (S3). Also, you'd naively expect prefetching to help locally since you'd do less work (you avoid re-parsing the footer when reading different row groups out of the same file multiple times).

Over simplifying things, prefetching slowed things down because it's (surprisingly?) expensive to copy a FileMetaData object. This pain was compounded in cudf-polars because we run concurrent read_parquets in a thread pool, and the GIL was held for these expensive copies.

(Note: why are we copying in the first place? IIUC, it's because the parquet reader currently mutates something on the object in read_parquet, so it needs an owned FileMetaData.)

  • This PR moves FileMetaData footers into Python without holding the GIL. This lets concurrent prefetches happen more... concurrently. See https://github.com/user-attachments/assets/c0f34117-8483-413a-a3de-e101a0093c12 for an nsys profile screenshot showing GIL contention.

  • After this fix, prefetching is still a clear win on S3 but there's still a ~16% penalty on NVMe. Rather than holding off entirely, this PR enables prefetching by default only for remote URIs (s3://, gs://, etc.). And queries that mix remote and local reads will only prefetch for the remote reads if metedata prefetching is unset (default).

Benchmarks (SF100, all 22 TPC-H queries, 256 kvikio threads):

Storage prefetch Total Mean
NVMe off 9.47s
NVMe on 10.95s (+15.6%)
S3 off 75.66s
S3 on 54.98s (-27.3%)

@TomAugspurger TomAugspurger added Performance Performance related issue improvement Improvement / enhancement to an existing function pylibcudf Issues specific to the pylibcudf package labels Aug 5, 2026
@copy-pr-bot

copy-pr-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions github-actions Bot added libcudf Affects libcudf (C++/CUDA) code. Python Affects Python cuDF API. labels Aug 5, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Aug 5, 2026
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test 39cc045

@TomAugspurger TomAugspurger added the non-breaking Non-breaking change label Aug 5, 2026
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

This change did help with the metadata prefetching. But I still see GIL contention in Scan nodes:

image

@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test b4d6a87

This changes the ownership model of FileMetadata to own the footers via
unique_ptr and move them (under nogil) in the pylibcudf wrapper.

Additionally, we avoid an unnecessary copy in get_parquet_metadatas().
@TomAugspurger
TomAugspurger force-pushed the tom/parquet-metadata-gil-perf branch from b4d6a87 to 1ac2985 Compare August 6, 2026 13:20
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test 1ac2985

@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test af347e8

When page indexes are present, cloning the parquet footer FileMetaData object
can be expensive. This slows down `read_parquet` when the user provides a
prefetched metadata object.

This PR adds a new `read_page_indexes` parameter to `read_parquet_footers`
that controls whether page indexes are materialized. The default is `True`
matching the existing behavior.
@TomAugspurger
TomAugspurger force-pushed the tom/parquet-metadata-gil-perf branch from 3d8de45 to b3775f5 Compare August 6, 2026 17:53
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test f5525b5

@TomAugspurger TomAugspurger changed the title [PERF]: Avoid deep copies with GIL in pylibcudf read_parquet_footers [PERF]: Improve the performance of read_parquet with prefetched parquet file metadata. Aug 6, 2026
@TomAugspurger TomAugspurger changed the title [PERF]: Improve the performance of read_parquet with prefetched parquet file metadata. [PERF]: Improve the performance of plc.io.parquet.read_parquet with prefetched parquet file metadata. Aug 6, 2026

timer.start();
auto const metadatas = cudf::io::read_parquet_footers(sources);
auto const metadatas = cudf::io::read_parquet_footers(sources, write_page_index);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure this change is needed, but it shouldn't hurt.

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.

This is currently a no-op (you're telling the benchmark to read page indexes only if they are written), but we probably want to benchmark a path where the indexes are present but ignored (write_page_index==true, read_page_indexes==false) at some point…

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we should stop reading page indexes. So I removed that commit. This means that we're not quite at parity on-prem with prefetch metdata turned off vs on--although we are closer than we were before this PR. For that reason, I'm only switching it on by default for cloud. And I've relaxed the garuntee that the metadata will be prefetched and cached if the setting is turned on to handle the case where some scans are reading from remote sources and others are not.

I actually think what may align the on-prem results is when metadata prefetching happens concurrently with scan execution (reading bytes)--see #23131.

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.

#23131 would overlap the footer read with other operations, but the clone happens on the scan critical path, so cost remains an issue. One complementary approach could be to minimize what gets cloned: the reader only mutates the schema (repetition_type promotion and apply_arrow_schema()), so row-group and page-index data could in principle be shared. This would be a larger undertaking, and definitely out of scope for this PR. Worth filing a separate feature request to track, or noting this in #23131?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

mhm, lets use #23572 as the issue

@github-actions github-actions Bot added the cudf-polars Issues specific to cudf-polars label Aug 6, 2026
@TomAugspurger

Copy link
Copy Markdown
Contributor Author

/ok to test 0053fb4

@TomAugspurger

Copy link
Copy Markdown
Contributor Author

These might overlap conceptually with #23546.

@NVIDIA NVIDIA deleted a comment from copy-pr-bot Bot Aug 12, 2026
@Matt711

Matt711 commented Aug 12, 2026

Copy link
Copy Markdown
Member

/ok to test 93bccd7

@copy-pr-bot

copy-pr-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Matt711

Matt711 commented Aug 12, 2026

Copy link
Copy Markdown
Member

/ok to test 8b7563a

Comment thread python/cudf_polars/tests/test_config.py
Comment thread python/cudf_polars/cudf_polars/engine/core.py
Comment thread python/cudf_polars/cudf_polars/utils/config.py Outdated
def version(self):
"""Get the file format version."""
return self.c_obj.version
return dereference(self.c_obj).version

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.

Doesn't __new__ bypass __init__? Wouldn't FileMetaData.__new__(FileMetaData) work outside of from_libcudf?

Comment thread python/cudf_polars/cudf_polars/dsl/ir.py
Comment thread python/cudf_polars/cudf_polars/engine/core.py
Comment thread python/cudf_polars/cudf_polars/utils/config.py Outdated
Comment thread python/cudf_polars/cudf_polars/dsl/utils/io.py

timer.start();
auto const metadatas = cudf::io::read_parquet_footers(sources);
auto const metadatas = cudf::io::read_parquet_footers(sources, write_page_index);

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.

#23131 would overlap the footer read with other operations, but the clone happens on the scan critical path, so cost remains an issue. One complementary approach could be to minimize what gets cloned: the reader only mutates the schema (repetition_type promotion and apply_arrow_schema()), so row-group and page-index data could in principle be shared. This would be a larger undertaking, and definitely out of scope for this PR. Worth filing a separate feature request to track, or noting this in #23131?

Comment thread python/cudf_polars/cudf_polars/utils/config.py Outdated
Comment thread python/cudf_polars/tests/test_config.py
@Matt711

Matt711 commented Aug 13, 2026

Copy link
Copy Markdown
Member

/ok to test b545203

@Matt711
Matt711 requested review from igorpeshansky and vuule August 13, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cudf-polars Issues specific to cudf-polars improvement Improvement / enhancement to an existing function libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change Performance Performance related issue pylibcudf Issues specific to the pylibcudf package Python Affects Python cuDF API.

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Enable cudf-polars parquet metadata prefetching by default on the cloud

7 participants