Skip to content

Rename cudf-polars max_io_threads configuration and change meaning - #23569

Merged
rapids-bot[bot] merged 8 commits into
NVIDIA:mainfrom
wence-:wence/fea/remove-max-io-threads
Aug 10, 2026
Merged

Rename cudf-polars max_io_threads configuration and change meaning#23569
rapids-bot[bot] merged 8 commits into
NVIDIA:mainfrom
wence-:wence/fea/remove-max-io-threads

Conversation

@wence-

@wence- wence- commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Description

The cudf-polars streaming configuration option max_io_threads is misnamed for two reasons:

  1. It has nothing to do with the number of threads we use anywhere;
  2. It isn't the max of anything.

The idea behind max_io_threads is to limit to maximum amount of concurrency available in read tasks during streaming execution. The idea is that we find the number of unique Scan nodes in an execution DAG and then let them each have at most max(1, max_io_threads // nscans) read tasks in flight. At the point we assign concurrency to reads we don't take into consideration how large the file is: does it even need more than one IO task, for example. Additionally, if we have more than max_io_threads unique Scan nodes, then we can actually have nscans concurrent IO tasks.

Really what we're trying to do is avoid launching too many read tasks to blow through memory limits. In read_chunk we're already suspending before launching a read to obtain a reservation for the output size, so we already have most of the machinery we want.

To fix the above deficiencies, we make two changes:

  1. Rename max_io_threads to max_concurrent_io_tasks and document that it applies per-Scan rather than globally;
  2. Tweak the reservation suspension to also request some space for the temporaries we'll need in reading the output chunk.

Additionally, I add logging to the reads.

Finally, since the rapidsmpf C++ read_parquet actor has never supported all the use cases we use in cudf-polars, and had different behaviour in terms of numbers of concurrent tasks, just remove Python bindings to it. There were no other Python consumers, the existing cudf-streaming C++ benchmarks can still use the C++ actor.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@wence-
wence- requested review from a team as code owners August 6, 2026 11:09
@wence- wence- added improvement Improvement / enhancement to an existing function breaking Breaking change labels Aug 6, 2026
@wence-
wence- requested a review from rjzamora August 6, 2026 11:09
@github-actions github-actions Bot added Python Affects Python cuDF API. cudf-polars Issues specific to cudf-polars labels Aug 6, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added configurable concurrent I/O tasks for streaming reads, defaulting to 2.
    • Added memory-aware admission control and I/O timing events.
    • Added configuration through executor options or the CUDF_POLARS__EXECUTOR__MAX_CONCURRENT_IO_TASKS environment variable.
  • Changes

    • Streaming scans now use the standard scan path.
  • Breaking Changes

    • Renamed the streaming concurrency setting to max_concurrent_io_tasks.
    • Removed native Parquet streaming configuration and related public Parquet APIs.

Walkthrough

The PR adds configurable concurrent streaming I/O tasks with memory admission tracing. It renames the executor option, removes native Parquet scan support and bindings, updates documentation and interfaces, and adds integration coverage for I/O-task admission ordering.

Changes

Streaming I/O concurrency and scan execution

Layer / File(s) Summary
I/O concurrency configuration
python/cudf_polars/cudf_polars/dsl/tracing.py, python/cudf_polars/cudf_polars/engine/options.py, python/cudf_polars/cudf_polars/utils/config.py, python/cudf_polars/docs/overview.md, python/cudf_polars/tests/streaming/test_options.py, python/cudf_polars/tests/test_config.py
Adds Scope.IO_TASK and the max_concurrent_io_tasks option with CLI, environment-variable, validation, documentation, and tests.
I/O admission and scan wiring
python/cudf_polars/cudf_polars/streaming/actor_graph/*, python/cudf_polars/tests/streaming/test_tracing.py, python/cudf_polars/tests/streaming/test_parallel.py
Passes configured concurrency into scan nodes. Reads reserve memory, record timing metadata, emit I/O-task traces, and produce TableChunk results.
Native Parquet path removal
python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py, python/cudf_polars/cudf_polars/streaming/io.py, python/cudf_polars/cudf_polars/utils/config.py, python/cudf_polars/docs/cudf-polars-mp.md, python/cudf_polars/tests/streaming/test_scan.py, python/cudf_streaming/*
Removes native Parquet selection, related configuration and benchmark flags, public Parquet bindings, and associated tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: tomaugspurger

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR renames the option but does not remove it or derive concurrency from spill-device limits and target partition size as required by issue #23552. Remove the public concurrency option and derive producer concurrency from spill-device limits and target partition size; add benchmark coverage for OOM safety.
Out of Scope Changes check ⚠️ Warning Removing the cudf_streaming.parquet Python bindings is not required by the linked issue and adds a separate public API removal. Move the Python binding removal to a separate issue and pull request, or link an issue that explicitly requires this API removal.
Docstring Coverage ⚠️ Warning Docstring coverage is 45.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main configuration rename and behavior change.
Description check ✅ Passed The description explains the configuration rename, concurrency changes, memory reservations, logging, API removal, tests, and documentation updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py (1)

480-483: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Add a deprecation path for removed configuration options.

RunConfig is public. These lines remove native_parquet and max_io_threads from its constructor and the benchmark CLI. Existing callers will fail without a migration message.

Retain deprecated compatibility handling for one release. Emit a deprecation warning. Do not silently map max_io_threads to max_concurrent_io_tasks, because the option now has per-Scan semantics. Give --native-parquet an explicit migration error after the warning because no equivalent remains.

As per coding guidelines, “Detect and flag API breaking changes to public methods/attributes without deprecation warnings.”

Also applies to: 627-632, 658-661, 707-710, 737-740, 2032-2035

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py` around lines
480 - 483, Add one-release compatibility handling to the public RunConfig
constructor and benchmark CLI for removed native_parquet and max_io_threads
options. Emit deprecation warnings for both; reject native_parquet with an
explicit migration error because no equivalent exists, and accept max_io_threads
only as deprecated input without mapping it to max_concurrent_io_tasks, whose
semantics are per-Scan. Apply this consistently to the referenced
argument-parsing and configuration paths.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/cudf_polars/cudf_polars/dsl/tracing.py`:
- Line 59: Update the affected type unions in engine/options.py to place None
last, resolving all three Ruff RUF036 errors while preserving the existing types
and behavior.

In `@python/cudf_polars/cudf_polars/streaming/io.py`:
- Line 531: Update the type annotations at lines 752 and 809 in
python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py to use pl.GPUEngine
| None, resolving both Ruff RUF036 errors; no direct change is required at
python/cudf_polars/cudf_polars/streaming/io.py:531-531.

In `@python/cudf_polars/cudf_polars/utils/config.py`:
- Around line 810-812: The public configuration rename must retain deprecated
compatibility. In python/cudf_polars/cudf_polars/utils/config.py:810-812, accept
max_io_threads as an alias for max_concurrent_io_tasks with deterministic
conflict handling, DeprecationWarning, and environment-variable migration; in
python/cudf_polars/cudf_polars/engine/options.py:348-350, preserve the same
alias across StreamingOptions, argparse, and environment parsing; in
python/cudf_polars/cudf_polars/utils/config.py:274-303, retain
ParquetOptions.use_rapidsmpf_native or detect its legacy environment variable
and issue a clear migration warning. Document the deprecation period and planned
removal version.
- Around line 900-901: Update the validation for max_concurrent_io_tasks in the
relevant configuration class to accept only positive, non-boolean integers;
reject 0, negative values, and False with the existing TypeError behavior, and
add coverage for each rejected case.

In `@python/cudf_polars/docs/overview.md`:
- Around line 413-417: Update the documentation around max_concurrent_io_tasks
to explicitly note that max_io_threads was renamed and that concurrency is now
limited independently per scan node rather than globally. Add migration guidance
telling users to replace the removed option with max_concurrent_io_tasks so
existing configurations do not silently use the default.

In `@python/cudf_polars/tests/streaming/test_tracing.py`:
- Around line 180-187: Update the subprocess handling around Popen and
communicate in the test helper to catch TimeoutExpired, call proc.kill(),
collect remaining output with communicate(), and fail the test after cleanup.
Preserve the existing returncode and output handling when communicate completes
within the timeout, ensuring Popen.__exit__ cannot block indefinitely.

---

Outside diff comments:
In `@python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py`:
- Around line 480-483: Add one-release compatibility handling to the public
RunConfig constructor and benchmark CLI for removed native_parquet and
max_io_threads options. Emit deprecation warnings for both; reject
native_parquet with an explicit migration error because no equivalent exists,
and accept max_io_threads only as deprecated input without mapping it to
max_concurrent_io_tasks, whose semantics are per-Scan. Apply this consistently
to the referenced argument-parsing and configuration paths.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4dd0f261-0935-4d78-b688-d5cdf2a31def

📥 Commits

Reviewing files that changed from the base of the PR and between 35115f3 and 423a531.

📒 Files selected for processing (19)
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_tracing.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_streaming/cudf_streaming/__init__.py
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
💤 Files with no reviewable changes (5)
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/init.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py

Comment thread python/cudf_polars/cudf_polars/dsl/tracing.py
Comment thread python/cudf_polars/cudf_polars/streaming/io.py
Comment thread python/cudf_polars/cudf_polars/utils/config.py Outdated
Comment thread python/cudf_polars/cudf_polars/utils/config.py
Comment thread python/cudf_polars/docs/overview.md
Comment thread python/cudf_polars/tests/streaming/test_tracing.py
@wence-
wence- force-pushed the wence/fea/remove-max-io-threads branch from 423a531 to 512d408 Compare August 6, 2026 11:40
@wence-
wence- requested a review from a team as a code owner August 6, 2026 11:40
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@github-actions github-actions Bot added the CMake CMake build issue label Aug 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/cudf_streaming/CMakeLists.txt`:
- Line 40: Retain cudf_streaming/parquet.pyx in the CMake cython_sources list
because io.py still imports Filter and read_parquet from cudf_streaming.parquet.
Do not remove the Parquet binding until that consumer is removed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7c2c3c0c-119d-46bd-acbf-e3e419891e2b

📥 Commits

Reviewing files that changed from the base of the PR and between 35115f3 and 512d408.

📒 Files selected for processing (20)
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_tracing.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_streaming/CMakeLists.txt
  • python/cudf_streaming/cudf_streaming/__init__.py
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
💤 Files with no reviewable changes (5)
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/init.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_polars/tests/test_config.py

Comment thread python/cudf_streaming/CMakeLists.txt Outdated
This was only implemented for some C++-only benchmarks. It is not used by
default (or under most circumstances) in cudf-polars and has a number of
performance problems. So we can purge a load of code we're not using.
@wence-
wence- force-pushed the wence/fea/remove-max-io-threads branch from 512d408 to dcd022b Compare August 6, 2026 13:30
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Comment thread python/cudf_polars/cudf_polars/utils/config.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
python/cudf_polars/cudf_polars/streaming/actor_graph/io.py (2)

530-540: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the 2 * reservation factor.

_process_and_send_chunk explains each reservation multiplier in an inline comment. This branch uses an unexplained factor of two for non-DataFrameScan reads. Add a short comment that states the second estimate covers decode temporaries, so future tuning does not guess.

♻️ Proposed comment
+    # Reserve the retained output plus, for file scans, one extra estimate for
+    # transient decode buffers. DataFrameScan slices need no decode scratch.
     reservation_bytes = (
         estimated_chunk_bytes
         if isinstance(scan, DataFrameScan)
         else 2 * estimated_chunk_bytes
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/streaming/actor_graph/io.py` around lines 530
- 540, Add a concise inline comment beside the non-DataFrameScan reservation
multiplier in the reservation_bytes calculation, stating that the additional
estimated chunk allocation covers decode temporaries. Leave the existing
reservation logic unchanged.

683-697: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the leftover nodes construction.

Only one node remains after the native Parquet path removal. Build nodes as a literal instead of an empty dict followed by an assignment. The ch_out temporary can also be inlined.

♻️ Proposed simplification
-    ch_out = channels[ir].reserve_input_slot()
-    nodes: dict[IR, list[Any]] = {}
-
-    nodes[ir] = [
-        scan_node(
-            rec.state["context"],
-            ir,
-            rec.state["ir_context"],
-            ch_out,
-            num_producers=num_producers,
-            estimated_chunk_bytes=(
-                plan.estimated_chunk_bytes or executor.target_partition_size
-            ),
-        )
-    ]
+    nodes: dict[IR, list[Any]] = {
+        ir: [
+            scan_node(
+                rec.state["context"],
+                ir,
+                rec.state["ir_context"],
+                channels[ir].reserve_input_slot(),
+                num_producers=num_producers,
+                estimated_chunk_bytes=(
+                    plan.estimated_chunk_bytes or executor.target_partition_size
+                ),
+            )
+        ]
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/streaming/actor_graph/io.py` around lines 683
- 697, In the surrounding graph-building function, simplify the single-node
setup by constructing nodes directly as a literal containing the scan_node
result, and inline the reserve_input_slot() call instead of assigning ch_out.
Preserve all existing scan_node arguments and the nodes mapping shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@python/cudf_polars/cudf_polars/streaming/actor_graph/io.py`:
- Around line 530-540: Add a concise inline comment beside the non-DataFrameScan
reservation multiplier in the reservation_bytes calculation, stating that the
additional estimated chunk allocation covers decode temporaries. Leave the
existing reservation logic unchanged.
- Around line 683-697: In the surrounding graph-building function, simplify the
single-node setup by constructing nodes directly as a literal containing the
scan_node result, and inline the reserve_input_slot() call instead of assigning
ch_out. Preserve all existing scan_node arguments and the nodes mapping shape.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e39eebdb-cfcd-4600-a0e3-df859c5b9c0a

📥 Commits

Reviewing files that changed from the base of the PR and between 89de7b4 and dcd022b.

📒 Files selected for processing (20)
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_tracing.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_streaming/CMakeLists.txt
  • python/cudf_streaming/cudf_streaming/__init__.py
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
💤 Files with no reviewable changes (5)
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/init.py
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_streaming/CMakeLists.txt
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/cudf_polars/engine/options.py

@rjzamora rjzamora 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.

Definitely makes sense to rename this and to remove the native-IO path (for now).

We originally limited the number of producers on each scan to 1 when the number of total scan actors was >= max_io_threads. With these changes, the total number of producers will be much larger for queries with multiple scan actors. Are you expecting the new memory-reservation tweaks to make this less risky? Have you tested this on a single H100 at all?

Comment thread python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
Comment thread python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
wence- added 5 commits August 6, 2026 17:05
This now limits the number of concurrent scan tasks per logical parquet
scan node in the execution DAG.
Previously we only waited for a memory reservation for the estimated output
size. But reading files also typically needs some temporary allocations, so
estimate that at the size of the output chunk too.
@wence-
wence- force-pushed the wence/fea/remove-max-io-threads branch from dcd022b to 0e8cd37 Compare August 6, 2026 16:07
@wence-

wence- commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

We originally limited the number of producers on each scan to 1 when the number of total scan actors was >= max_io_threads. With these changes, the total number of producers will be much larger for queries with multiple scan actors. Are you expecting the new memory-reservation tweaks to make this less risky? Have you tested this on a single H100 at all?

I ran on H100 at SF1K. On main, with all defaults the pdsh benchmarks take ~148s lukewarm. On this branch, with max-concurrent-io-tasks set to 2 (the default) and all defaults, we take ~138s. So a small gain.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
python/cudf_polars/cudf_polars/streaming/io.py (1)

531-531: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the streaming executor’s chunked override.

ParquetOptions.chunked is public and defaults to True, but streaming scan lowering always replaces it with False. Update its documentation to state this behavior, or warn when streaming receives chunked=True.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/streaming/io.py` at line 531, Document in the
ParquetOptions API that streaming scan lowering overrides chunked=True to False,
or add a warning when the streaming executor receives chunked=True; anchor the
change around the parquet_options replacement in the streaming scan lowering
path and preserve the existing chunked=False behavior.

Source: Coding guidelines

python/cudf_polars/cudf_polars/streaming/actor_graph/io.py (1)

11-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant IR import from the TYPE_CHECKING block. Scope.IO_TASK.value is correctly defined as "io_task".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf_polars/cudf_polars/streaming/actor_graph/io.py` around lines 11 -
26, Remove the unused IR import from the TYPE_CHECKING imports in the module,
leaving the existing Scope.IO_TASK.value usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/cudf_polars/tests/streaming/test_tracing.py`:
- Line 164: Update the generated query string in the test around q and
pl.scan_parquet so source is embedded using Python repr formatting rather than
raw interpolation, ensuring paths containing quotes or backslashes remain valid
Python string literals.

---

Nitpick comments:
In `@python/cudf_polars/cudf_polars/streaming/actor_graph/io.py`:
- Around line 11-26: Remove the unused IR import from the TYPE_CHECKING imports
in the module, leaving the existing Scope.IO_TASK.value usage unchanged.

In `@python/cudf_polars/cudf_polars/streaming/io.py`:
- Line 531: Document in the ParquetOptions API that streaming scan lowering
overrides chunked=True to False, or add a warning when the streaming executor
receives chunked=True; anchor the change around the parquet_options replacement
in the streaming scan lowering path and preserve the existing chunked=False
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c2d009d2-2c57-414e-99df-24a37e015b74

📥 Commits

Reviewing files that changed from the base of the PR and between 89de7b4 and 0e8cd37.

📒 Files selected for processing (20)
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/io.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py
  • python/cudf_polars/cudf_polars/streaming/io.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_polars/tests/streaming/test_tracing.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_streaming/CMakeLists.txt
  • python/cudf_streaming/cudf_streaming/__init__.py
  • python/cudf_streaming/cudf_streaming/parquet.pyi
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
💤 Files with no reviewable changes (5)
  • python/cudf_polars/tests/streaming/test_scan.py
  • python/cudf_streaming/cudf_streaming/tests/test_read_parquet.py
  • python/cudf_streaming/cudf_streaming/init.py
  • python/cudf_streaming/cudf_streaming/parquet.pyx
  • python/cudf_streaming/cudf_streaming/parquet.pyi
🚧 Files skipped from review as they are similar to previous changes (12)
  • python/cudf_polars/docs/overview.md
  • python/cudf_polars/docs/cudf-polars-mp.md
  • python/cudf_streaming/CMakeLists.txt
  • python/cudf_polars/tests/streaming/test_options.py
  • python/cudf_polars/cudf_polars/dsl/tracing.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/core.py
  • python/cudf_polars/tests/streaming/test_parallel.py
  • python/cudf_polars/cudf_polars/streaming/actor_graph/dispatch.py
  • python/cudf_polars/cudf_polars/engine/options.py
  • python/cudf_polars/tests/test_config.py
  • python/cudf_polars/cudf_polars/utils/config.py
  • python/cudf_polars/cudf_polars/streaming/benchmarks/utils.py

Comment thread python/cudf_polars/tests/streaming/test_tracing.py
Comment thread python/cudf_polars/cudf_polars/engine/options.py Outdated
Comment thread python/cudf_polars/cudf_polars/engine/options.py Outdated
parquet_options = config_options.parquet_options
partition_info = rec.state["partition_info"][ir]
num_producers = rec.state["max_io_threads"]
num_producers = rec.state["max_concurrent_io_tasks"]

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.

cc @Matt711 I think we'll need to thread this new option to apply to SplitScan/FusedScan nodes for the hybrid reader?

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.

A scan_node is made with this many producers. In this branch, that feeds in to the number the read_chunk call. Each read_chunk then suspends for a memory reservation. So if you're keeping that same setup I don't think you need to do anything.

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.

Yeah, I don't think the hybrid-scan work is adding/changing any references to "max_io_threads" or it's behavior, so you should be good.

With that said, it may be worth using this branch to run a cloud benchmark (if it's not too painful). The new default behavior seems to be a win for local storage, but I don't think we've measured in the cloud yet?

@wence-

wence- commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit cd60e06 into NVIDIA:main Aug 10, 2026
133 of 134 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Aug 10, 2026
@wence-
wence- deleted the wence/fea/remove-max-io-threads branch August 10, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking change CMake CMake build issue cudf-polars Issues specific to cudf-polars improvement Improvement / enhancement to an existing function Python Affects Python cuDF API.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[PERF]: Remove max_io_threads from the public API and derive it automatically

7 participants