Skip to content

Avoid blocking memory reservations in actor coroutines - #23892

Open
madsbk wants to merge 4 commits into
NVIDIA:mainfrom
madsbk:avoid-blocking-reservations-in-actor-coroutines
Open

Avoid blocking memory reservations in actor coroutines#23892
madsbk wants to merge 4 commits into
NVIDIA:mainfrom
madsbk:avoid-blocking-reservations-in-actor-coroutines

Conversation

@madsbk

@madsbk madsbk commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Every actor on a rank shares one event loop, so an actor that reserves device memory synchronously stalls all the others while it spills. This moves every such reservation that runs inside an actor coroutine off the blocking path. They take their memory from reserve_memory() now, so a request that cannot be satisfied queues alongside the other actors' and is served by priority instead of spilling on the spot. That gives these sites memory backpressure as well as an unblocked loop, since an actor waiting on a reservation lets the ones that can release memory run first.

Unspilling table chunks

TableChunk.make_available_and_spill() spills synchronously. Twelve call sites move to the awaitable make_table_chunks_available_or_wait(), which suspends until a reservation is granted so the other actors can run and release memory meanwhile.

AllGatherManager.Inserter.insert and _unpack_remote_partition become coroutines, updating eight call sites across join.py, repartition.py, sort.py, utils.py, ordering.py and the AllGather tests. _unpack_remote_partition also takes the context rather than a buffer resource. Neither is public API.

Behavior change

The unspill calls previously passed allow_overbooking=True. They now fall through to the allow_overbooking_by_default configuration option, matching every existing make_table_chunks_available_or_wait() call site. That option ships as true, so under the default the only difference is that these sites wait for memory before overbooking rather than overbooking immediately. With the option set to false they can raise where previously they could not.

@madsbk madsbk self-assigned this Aug 31, 2026
@madsbk madsbk added improvement Improvement / enhancement to an existing function breaking Breaking change labels Aug 31, 2026
@github-actions github-actions Bot added Python Affects Python cuDF API. cudf-polars Issues specific to cudf-polars labels Aug 31, 2026
@madsbk
madsbk force-pushed the avoid-blocking-reservations-in-actor-coroutines branch from 140c1e7 to 5826e82 Compare August 31, 2026 10:51
@madsbk
madsbk marked this pull request as ready for review August 31, 2026 11:57
@madsbk
madsbk requested a review from a team as a code owner August 31, 2026 11:57
@madsbk
madsbk requested a review from nirandaperera August 31, 2026 11:57
@NVIDIA NVIDIA deleted a comment from copy-pr-bot Bot Aug 31, 2026
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

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: 44ae3843-5402-4199-ab4f-4ef6a7cebdfd

📥 Commits

Reviewing files that changed from the base of the PR and between adf03d4 and ccac165.

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

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


📝 Walkthrough

Summary by CodeRabbit

  • Performance

    • Improved streaming data movement and aggregation by coordinating memory availability before processing chunks.
    • Reduced unnecessary spilling, copying, and temporary memory over-allocation across joins, shuffles, sorting, unions, fanout, grouping, and output operations.
    • Skipped unnecessary memory work for empty chunks.
  • Reliability

    • Improved handling of memory pressure during repartitioning, remote data processing, and file output.
    • Enhanced asynchronous processing so operations wait safely when resources are temporarily unavailable.
    • Improved fanout memory placement across device, pinned-host, and host memory.

Walkthrough

The streaming actor graph now uses asynchronous chunk availability and explicit memory reservations. AllGather packing and remote unpacking reserve temporary memory before processing. Collective, join, fanout, sink, origin-stamp, and union paths use the updated coordination flow.

Changes

Streaming memory coordination

Layer / File(s) Summary
Asynchronous AllGather reservation
python/cudf_polars/.../collectives/allgather.py
AllGather insertion is asynchronous. Packing and unpacking reserve estimated memory before processing.
AllGather caller updates
python/cudf_polars/.../collectives/sort.py, python/cudf_polars/.../collectives/join.py, python/cudf_polars/.../repartition.py, python/cudf_polars/.../utils.py, python/cudf_polars/tests/streaming/test_allgather.py
All AllGather insertion callers and the test now await insert.
Collective input memory accounting
python/cudf_polars/.../collectives/ordering.py, python/cudf_polars/.../collectives/shuffle.py, python/cudf_polars/.../collectives/sort.py
Remote unpacking and incoming shuffle or ordering chunks use asynchronous availability handling with explicit reservations. Empty ordering chunks skip memory checks and reservations.
Actor-graph memory paths
python/cudf_polars/.../io.py, python/cudf_polars/.../join.py, python/cudf_polars/.../nodes.py, python/cudf_polars/.../over.py, python/cudf_polars/.../union.py
Sinks, joins, fanout, origin stamping, and union relay paths use reservation and memory-delta tracking instead of spill-based overbooking.

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

Merge Risk: ⚪ Minimal · up to ccac1

The change moves actor memory reservations onto an awaitable path without any supplied actionable merge-blocking risk; it is merge-ready after normal checks and review.

Suggested reviewers: nirandaperera

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 12 files.
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.
Description check ✅ Passed The description clearly explains the asynchronous memory-reservation changes, updated call sites, coroutine conversions, and behavior change.
Title check ✅ Passed The title clearly and concisely summarizes the main change: avoiding blocking memory reservations within actor coroutines.
✨ 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 added a commit to madsbk/cudf that referenced this pull request Aug 31, 2026

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

Breaking change
Two functions become coroutines

These aren't part of the public cudf-polars API, so if you were basing the "breaking" label off those two I think we can remove it.

Comment on lines +116 to +122
# Representation change: the packed input is consumed as the
# unpacked table is produced, at roughly the same size.
reservation = await reserve_memory(
self.context,
unpack_and_concat_cost(partitions),
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.

Can you explain this reservation? IIUC, we're making a change to ensure that we have room for both partitions (already in memory) and the unpack_and_concat form.

But the comment says that "the packed input is consumed as the unpacked table is produces". So are we freeing from partitions as the unpacked form is produced or not?

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.

Updated the comment, the same size was refering to the net_memory_delta argument. Fixed in: ccac165

@madsbk

madsbk commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Breaking change
Two functions become coroutines

These aren't part of the public cudf-polars API, so if you were basing the "breaking" label off those two I think we can remove it.

It was, removed

@madsbk madsbk added non-breaking Non-breaking change and removed breaking Breaking change labels Aug 31, 2026
@madsbk
madsbk requested a review from TomAugspurger August 31, 2026 14:25
madsbk added a commit to madsbk/cudf that referenced this pull request Aug 31, 2026
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 non-breaking Non-breaking change Python Affects Python cuDF API.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants