Repository navigation
[Bugfix][Distributed] Preserve symmetric collective outputs #58154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d8a8bc6
cfaf005
6d30c7b
228a9a7
93efe47
69556ef
b547c07
ed5c669
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the PR's description: “does not repeat allocation” should refer specifically to symmetric scratch allocation.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the PR description to clarify that cached symmetric scratch and window registration are reused, while each |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -559,11 +559,11 @@ def _get_symm_scratch( | |
| call (~0.5 ms/RS+AG pair, dwarfing the NVLS transfer itself). Instead we | ||
| allocate once per ``(role, shape, dtype)``, register once, and reuse. | ||
|
|
||
| Safe for serial (eager) sequence parallelism: each collective's result | ||
| is consumed on the same stream before the next same-role collective | ||
| reuses the buffer. Distinct roles (e.g. ``rs_in`` vs ``ag_out``, both | ||
| full-size) get distinct buffers so a reduce-scatter input copy never | ||
| clobbers a still-live all-gather output. | ||
| These buffers are internal scratch. Returning one would let a later | ||
| collective with the same cache key overwrite an earlier live result. | ||
| Distinct roles (e.g. ``rs_in`` vs ``ag_out``, both full-size) get | ||
| distinct buffers so a reduce-scatter input copy never clobbers a | ||
| still-live all-gather output. | ||
| """ | ||
| from vllm.distributed.device_communicators.pynccl_allocator import ( | ||
| nccl_symm_mem_context, | ||
|
|
@@ -616,7 +616,7 @@ def _reduce_scatter_symm_mem( | |
| symm_input.copy_(input_tensor) | ||
|
|
||
| pynccl_comm.reduce_scatter(symm_output, symm_input) | ||
| return symm_output | ||
| return symm_output.clone() | ||
|
|
||
| def send(self, tensor: torch.Tensor, dst: int | None = None) -> None: | ||
| """Sends a tensor to the destination rank in a blocking way.""" | ||
|
|
@@ -815,7 +815,7 @@ def _all_gather_symm_mem(self, input_: torch.Tensor) -> torch.Tensor: | |
| "ag_out", out_size, input_.dtype, input_.device | ||
| ) | ||
| pynccl_comm.all_gather(symm_output, input_) | ||
| return symm_output | ||
| return symm_output.clone() | ||
|
Comment on lines
969
to
+970
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching the issue. However this means we will lose the zero copy behavior. To allow different use cases, let's add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @GirasoleY, thanks. My concern with adding Could we keep this PR focused on the ownership fix and pursue the copy cost separately?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah ideally we want to either allocate different buffers or be smart about the buffer regions so collective that can race each other do not overlap their symm buffer region.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, that sounds like a good longer-term direction. Could we merge this PR as an initial correctness fix and address avoiding the extra copy in a follow-up? That would require ensuring buffers aren't reused while earlier results are still live. |
||
|
|
||
| def _all_gather_batched_symm_mem( | ||
| self, inputs: list[torch.Tensor] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we add CUDA-graph coverage for both operations? Capture two same-key calls, retain both outputs, then replay with changed inputs and verify both results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added CUDA graph coverage in
cfaf005: both AllGather and ReduceScatter capture two same-key calls, retain both outputs, and verify them after two replays with changed inputs. The exact PR head passed both tests on 2xGB200 with no skips. NCCL logs confirmed the symmetric paths.