Skip to content

Fix nonblocking P2P staging on MPS - #8301

Closed
FU-max-boop wants to merge 1 commit into
deepspeedai:masterfrom
FU-max-boop:fix/nonblocking-p2p-staging
Closed

Fix nonblocking P2P staging on MPS#8301
FU-max-boop wants to merge 1 commit into
deepspeedai:masterfrom
FU-max-boop:fix/nonblocking-p2p-staging

Conversation

@FU-max-boop

Copy link
Copy Markdown
Contributor

Summary

  • route deepspeed.comm.isend and irecv through the backend's nonblocking methods
  • treat point-to-point MPS staging as inherently asynchronous
  • keep staged send buffers alive and copy received data back exactly once when the underlying Work completes
  • preserve Work polling, result, future, and error semantics while restoring original tensor identities

This follows the unresolved MPS review note on #8293: #8293 (comment). The existing wrapper only detects collectives with an async_op argument, but isend and irecv are asynchronous by contract and irecv has no such parameter. In addition, the public wrappers currently dispatch them to blocking backend methods.

Validation

  • pytest tests/unit/comm/test_p2p.py — 16 passed
  • pytest --forked tests/unit/comm/test_p2p.py — 16 passed
  • pre-commit run --files deepspeed/comm/comm.py deepspeed/comm/torch.py tests/unit/comm/test_p2p.py

The tests use deterministic fake MPS tensors and work handles to cover wait, polling, result and future completion, failure paths, no-grad copy-back, exactly-once behavior, and staging lifetime.

Signed-off-by: Fu Xiaonan <ht3fudatou@163.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c495d624b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread deepspeed/comm/torch.py
Comment on lines +146 to +147
if result is False:
self._discard()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep staging alive when a timed wait returns false

When the underlying Work.wait(timeout=...) returns False because the operation is still pending, _discard() permanently removes the completion callback and releases the staged CPU buffers. A later successful wait can therefore neither safely finish the transfer nor copy an irecv result back to the MPS tensor, causing lost receive data and potentially dropping an in-flight send buffer; retain the staging state on a timeout and only discard it after a confirmed terminal failure.

Useful? React with 👍 / 👎.

Comment thread deepspeed/comm/comm.py
def isend(tensor, dst, group=None, tag=0, prof=False, log_name='isend', debug=get_caller_func()):
global cdb
return cdb.send(tensor=tensor, dst=dst, group=group, tag=tag)
return cdb.isend(tensor=tensor, dst=dst, group=group, tag=tag)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the required sign-off to this non-merge commit

Commit b7ee546299de3a8ade46b1f7d5ee5ac3d1c0d3bd is a non-merge commit but its message has no Signed-off-by trailer, so it does not meet this repository's commit requirements; recreate the commit with --signoff using the configured Git identity.

AGENTS.md reference: AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

@sfc-gh-truwase
sfc-gh-truwase requested a review from PKUWZP August 23, 2026 22:33

@PKUWZP PKUWZP left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks so much for the PR, a couple of things here:

  • The comm.py rerouting changes isend/irecv semantics for every backend (NCCL included) — it revives previously-dead async paths in the 1-bit comm modules. It's a global behavior change riding in an "MPS staging" PR; it should be called out prominently and watched on the CUDA CI, not just macOS.

  • StagedWork grew locks, a weakref identity map, future wrapping, and __getattr__ delegation (~100 lines) to preserve result()/get_future_result() semantics that no current DeepSpeed caller uses. I feel it add unnecessary complexity — callers only use .wait() and occasionally is_completed(). Can we drop the future/result machinery until something needs it.

@PKUWZP

PKUWZP commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

@FU-max-boop Also I found that tests are all based on FakeTensor, FakeWork, monkeypatched staging predicate etc. — they elaborate implementation details (copy counts, weakref retention) rather than behavior, and none of them tests real torch.distributed. A 2-rank gloo test with real Work objects (like the one I ran) would be stronger and could run on any CI, not just macOS.

@FU-max-boop

Copy link
Copy Markdown
Contributor Author

Thanks @PKUWZP for the detailed review and for carrying the narrower implementation in #8303. I agree with both review points: #8301's locks/weakrefs/future/result machinery is larger than the current call surface justifies, and its fake-heavy tests do not replace a real two-rank torch.distributed regression.

#8303 now carries the core routing change, focused MPS staging coverage, and a real two-rank Gloo path, including the CPU skip fix in 1504ea6. To avoid maintaining two competing implementations, I'm closing #8301 as superseded by the active #8303.

This is coordination, not a claim that #8303 has merged. I'll leave fix/nonblocking-p2p-staging exactly as-is: no branch deletion and no force-push, so the work remains available for reference or reopening if needed. I'll take only the remaining minimal Work-contract questions into a separate maintainer-agreed follow-up after #8303 settles.

Thanks again for the concrete feedback.

banxingmjj pushed a commit to openanolis/DeepSpeed that referenced this pull request Aug 24, 2026
…ync on MPS (deepspeedai#8303)

## Summary

Resolves @delock's review note on deepspeedai#8293
(deepspeedai#8293 (comment)):
`irecv` is asynchronous by contract and has no `async_op` parameter, so
the MPS CPU-staging wrapper must handle it explicitly.

Two fixes:

1. **`deepspeed/comm/comm.py`** — `isend`/`irecv` dispatched to the
*blocking* `cdb.send`/`cdb.recv` (since the original comm backend,
deepspeedai#1985). Callers got a blocking call and `recv`'s return value (the
source rank `int`) instead of a waitable handle, so
`dist.irecv(...).wait()` raised `AttributeError`. This affects every
backend, not just MPS — e.g. the 1-bit comm helpers
(`runtime/comm/{compressed,hccl,nccl}.py`) call
`dist.isend/irecv(...).wait()`. They now route to
`cdb.isend`/`cdb.irecv`.
2. **`deepspeed/comm/torch.py`** — with the routing fixed, the MPS
staging wrapper's copy-back decision (keyed on an `async_op` argument)
ran immediately for `irecv`, before the transfer completed. A new
`always_async` flag on `stage_on_cpu` defers the copy-back to the
handle's `wait()` for `isend`/`irecv`. `StagedWork.wait()` now also
returns the underlying work's wait result.

### Verified (M5 Max, macOS 26.3, torch 2.13)

- Real two-process gloo run with MPS tensors: on master, `dist.irecv`
returns an `int` and `.wait()` crashes; with this PR it returns a handle
and the buffer holds the correct payload after `wait()`.
- `DS_ACCELERATOR=mps pytest unit/comm/test_dist.py`: 10 passed
(multi-rank cases skip on 1 device).
- ZeRO-2/3 smoke training unaffected.

### Test

Adds `TestDistIsendIrecv` (world size 2) to the existing
`tests/unit/comm/test_dist.py`: rank 0 `isend`s, rank 1 `irecv`s, both
assert a waitable handle and verify the payload after `wait()`.
Backend-agnostic, so it exercises the routing fix on CUDA/CPU CI as
well.

### Relation to deepspeedai#8301

deepspeedai#8301 addresses the same note with a more extensive `StagedWork`
(futures, result identity restoration, weakref buffer tracking). This PR
makes the fix more concise and accurate: no current DeepSpeed users
calls `Work.result()`/`get_future()` on staged P2P ops, and the staged
CPU buffer for `isend` is kept alive by the deferred copy-back closure
until `wait()`. Huge Credit to @FU-max-boop for the thorough analysis of
the Work semantics and fixes.

---------

Signed-off-by: PKUWZP <zhipeng.rainbowserie@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants