Skip to content

Apply memory backpressure to the shuffle - #23886

Open
madsbk wants to merge 4 commits into
NVIDIA:mainfrom
madsbk:apply-memory-backpressure-to-the-shuffle
Open

Apply memory backpressure to the shuffle#23886
madsbk wants to merge 4 commits into
NVIDIA:mainfrom
madsbk:apply-memory-backpressure-to-the-shuffle

Conversation

@madsbk

@madsbk madsbk commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

The shuffle now takes its device memory from reserve_memory() on both sides, the four ShuffleManager.Inserter methods going in and extract_chunk coming out, so a request that cannot be satisfied queues alongside the other actors' and is served by priority. Before this both let the C++ side reserve and spill internally. cudf-polars already reserves this way for scans in actor_graph/io.py.

Breaking change

The insert methods and extract_chunk are coroutines now, and LocalRepartitioner._iter_chunks is an async generator. That updates twenty-one call sites across groupby.py, over.py, sort.py, shuffle.py and the shuffler tests.

Notes

Reserving inside the insert methods rather than at the call sites keeps the accounting in one place. That matters for insert_hash_keys and insert_index, whose Python-side reorder allocates a full table copy that nothing reserved before. They reserve partition_and_pack_cost(), which covers a reorder plus a pack, consume the reorder's share once it lands, and hand the remainder to split_and_pack().

The cost functions call cudf::packed_size(), which syncs the stream, and they run on the actor event loop. insert_hash and insert_split add one sync per chunk, insert_hash_keys and insert_index two, since they size the reorder separately from the pack. Passing a packed_bytes hint through the Cython layer would remove the second one, and partition_and_pack already takes that hint in C++.

_iter_chunks reserves per piece, so the TODO there about batching pieces up to target_partition_size would cut the reservation traffic as well as the unpack overhead.

@madsbk madsbk self-assigned this Aug 29, 2026
@madsbk madsbk added improvement Improvement / enhancement to an existing function breaking Breaking change labels Aug 29, 2026
@github-actions github-actions Bot added Python Affects Python cuDF API. cudf-polars Issues specific to cudf-polars labels Aug 29, 2026
@madsbk
madsbk force-pushed the apply-memory-backpressure-to-the-shuffle branch 2 times, most recently from e3090b3 to a3f477a Compare August 31, 2026 10:30
@madsbk
madsbk marked this pull request as ready for review August 31, 2026 11:41
@madsbk
madsbk requested a review from a team as a code owner August 31, 2026 11:41
@madsbk
madsbk requested a review from pentschev August 31, 2026 11:41
@coderabbitai

coderabbitai Bot commented Aug 31, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ff4d53ed-5071-4dea-9d47-4fa34b1ea60d

📥 Commits

Reviewing files that changed from the base of the PR and between 69c1036 and 5675c32.

📒 Files selected for processing (1)
  • python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


📝 Walkthrough

Summary by CodeRabbit

  • Performance

    • Improved streaming shuffle and repartition operations with asynchronous memory reservation and resource-aware data packing.
    • Enhanced memory cost estimation for more efficient partitioning and data movement.
  • Reliability

    • Updated sorting, grouping, windowing, and repartition workflows to handle asynchronous shuffle operations consistently.
    • Improved partitioning metadata handling and indexed data redistribution.
    • Expanded coverage for shuffle insertion, extraction, and repartitioning scenarios.

Walkthrough

The shuffle implementation now performs memory reservation, partitioning, packing, and unpacking asynchronously. Repartitioning and dependent operators await the new APIs. Window routing uses asynchronous indexed insertion. Shuffler tests cover asynchronous hash and index workflows.

Changes

Asynchronous shuffle memory management

Layer / File(s) Summary
Reservation-aware shuffle operations
python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py
Shuffle insertion and extraction estimate memory costs, reserve memory asynchronously, pass CUDA resources and reservations to operations, and track temporary reorder memory.
Async repartitioning and global shuffle wiring
python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py
Local chunk iteration, repartition insertion, and global shuffle extraction use asynchronous interfaces.
Operator integration and validation
python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/sort.py, python/cudf_polars/cudf_polars/streaming/actor_graph/groupby.py, python/cudf_polars/cudf_polars/streaming/actor_graph/over.py, python/cudf_polars/tests/streaming/test_shuffler.py
Sort, groupby, and window operations await shuffle calls. Window return routing uses payload and origin-rank extraction with indexed insertion. Shuffler tests await hash and index operations.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 5675c

The shuffle memory-reservation behavior and related async interfaces introduce no actionable merge-blocking risk at the current head.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding memory backpressure to the shuffle.
Description check ✅ Passed The description directly explains the shuffle memory reservations, asynchronous API changes, affected call sites, and implementation details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

coderabbitai[bot]

This comment was marked as resolved.

@madsbk
madsbk force-pushed the apply-memory-backpressure-to-the-shuffle branch from fbe65af to 4abe1d5 Compare August 31, 2026 14:04
@NVIDIA NVIDIA deleted a comment from copy-pr-bot Bot Aug 31, 2026
Comment thread python/cudf_polars/cudf_polars/streaming/actor_graph/collectives/shuffle.py Outdated
@madsbk
madsbk requested a review from pentschev September 1, 2026 07:04
Comment on lines +129 to 138
# Three allocations of the chunk's packed size: the key table, the
# reorder, then the pack.
chunk_nbytes = py_split_and_pack_cost(chunk.table_view(), chunk.stream, br)
reservation = await reserve_memory(
self._manager.context,
3 * chunk_nbytes,
# The chunk's data moves into shuffler-owned packed buffers,
# nothing lasting is added.
net_memory_delta=0,
)

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.

Could we add a brief comment explaining the 3 * chunk_nbytes assumption? Pointwise key expressions can expand their input, for example, string padding, replacement, or concatenation, so the evaluated key table is not necessarily bounded by the input chunk’s packed size. I do not think this PR needs to solve general expression-aware size estimation, but documenting the expected workload/invariant here would make the trade-off explicit and help us revisit it if this path encounters memory-pressure failures.

And could we also add a bounded-memory test for a computed shuffle key? It need not cover every expansion case, one representative expression that materializes a new key column would verify that this path waits for memory and completes correctly, rather than allocating outside the backpressure mechanism.

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.

Good point, added a comment explaining the 3 * chunk_nbytes assumption.

On the test, I tried a few approaches but they all ended up too intrusive and tied to internal details. A bounded-memory test does not actually distinguish the two cases here, since the buffer resource's limit is an accounting limit rather than an allocation barrier, so the unreserved version allocates and completes just the same. Asserting on the reservation itself works, but only by monkeypatching reserve_memory and pinning the exact multiplier, which locks the test to the current internals of this path.

Given the estimate is an approximation rather than a bound, I do not think a detailed test is worth it. The computed-key path itself is already covered end to end by test_join_non_col_keys_rapidsmpf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants