Fix: add missing end_sync barrier in cross_device_reduce_1stage - #3514
Merged
valarLip merged 1 commit intoJun 4, 2026
Merged
Conversation
Without this barrier, a fast rank can exit the kernel while a slow rank is still reading peer input via IPC. The fast rank's caller can then overwrite the input slot (e.g. PyTorch graph_pool reuse in a captured CUDA graph), causing the slow rank to read garbage and produce NaN / Inf in the AR output. All other AR kernels in this file already call end_sync before exit.
Contributor
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a correctness race in the custom IPC-based all-reduce kernel cross_device_reduce_1stage by adding the missing exit synchronization barrier (end_sync) before kernel return, aligning it with the other all-reduce variants in custom_all_reduce.cuh and preventing cross-rank buffer reuse from corrupting peer reads under CUDA-graph capture.
Changes:
- Add
end_sync<ngpus, true>(...)at the end ofcross_device_reduce_1stageto enforce a cross-rank exit barrier and avoid peer IPC reads racing with subsequent graph kernels overwriting the input slot.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| buf = next_buf; | ||
| } | ||
| end_sync<ngpus, true>(sg, self_sg, rank); |
valarLip
approved these changes
Jun 4, 2026
1 task
yixionghuo
pushed a commit
that referenced
this pull request
Aug 6, 2026
* Fix: add missing end_sync in cross_device_reduce_1stage (#3514) Without this barrier, a fast rank can exit the kernel while a slow rank is still reading peer input via IPC. The fast rank's caller can then overwrite the input slot (e.g. PyTorch graph_pool reuse in a captured CUDA graph), causing the slow rank to read garbage and produce NaN / Inf in the AR output. All other AR kernels in this file already call end_sync before exit. * fix: synchronize custom collectives before return (#4082) Co-authored-by: ColorsWind <14761584+ColorsWind@users.noreply.github.com> --------- Co-authored-by: Zhu Jiale <69138280+zovonoir@users.noreply.github.com> Co-authored-by: Yingyi Hao <42579422+jpy794@users.noreply.github.com> Co-authored-by: ColorsWind <14761584+ColorsWind@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3515 — see the issue for the full customer-facing context (Qwen3.5-397B-FP8 on SGLang+ATOM coredump report), reproduction steps, and the complete root-cause investigation.
Summary
cross_device_reduce_1stageis the only all-reduce kernel incsrc/include/custom_all_reduce.cuhthat does not callend_syncbefore kernel exit. This is a cross-rank write-after-read race: a fast rank can exit the kernel while a slow rank is still reading peer input via IPC, and the fast rank's caller (e.g. the next kernel in a captured CUDA graph that reuses the input slot through PyTorch'sgraph_pool) then overwrites the slot the slow rank is still reading. The slow rank's AR sum becomes garbage (NaN / Inf / unrelated values).This PR adds a single
end_synccall before kernel exit, matching the pattern already used by every other AR kernel in the same file.Symptoms in production
HSA_STATUS_ERROR_EXCEPTION(code0x1016) raised from_assert_async_cuda_kernelduring decode, scheduler process exits with code -6registered_input=True+ decode withtemperature > 0(sampling)temperature = 0) does not crash but silently produces garbage tokens —argmax(NaN)does not raise, whilemultinomial(softmax(NaN))triggers a CUDA-side probability-validity assertReproduced consistently on Qwen3.5-397B-A17B-FP8 + TP=4 + MI308X on ROCm 7.2.x.
Root cause
cross_device_reduce_1stagereads peer rank input directly via IPC:start_syncat kernel entry guarantees all ranks have entered the kernel (so producer kernels' writes to local input are visible). But there is no barrier at kernel exit, so:Asymmetry with other AR kernels
Every other AR kernel variant in
custom_all_reduce.cuhalready callsend_syncbefore exit:end_synccross_device_reduce_1stage_naivecross_device_reduce_2stagecross_device_reduce_2stage_naivecross_device_reduce_2stage_write_mode(variants)cross_device_reduce_1stageThis is the only kernel without it.
The fix
buf = next_buf; } + end_sync<ngpus, true>(sg, self_sg, rank); }Verification
We verified the fix with a controlled experiment using a deterministic AR input so any deviation is unambiguous bug evidence.
Setup
rocm/pytorch:rocm7.2.2_ubuntu22.04_py3.10_pytorch_release_2.9.1— the bug is ROCm-version-independent; we have reproduced it on both ROCm 7.2.2 and 7.2.3registered_input=TrueQwen3NextSparseMoeBlock.forward, replace the AR input withtorch.zeros_like(...) + 1.0. With 4 ranks each writing1.0, the AR sum must deterministically equal4.0at every element, every layer, every call. Any other observed value is a bug.Results
NaN,Inf,872,1168,−2.66×10³⁴, ...temperature=0.74.0as expected)temperature=0.7, deterministic probe4.0)temperature=0.70 / 2068mismatches with the fix vs13 / 1048without it gives a one-sided Fisher's exact p-value of approximately6.7×10⁻⁷(≈ 4.8 σ) against the null hypothesis that the fix has no effect.Trap confirmation that this is the kernel on the production path
Before applying the fix, we inserted
__builtin_trap()at the entry ofcross_device_reduce_1stageand all 4 ranks hitHSA_STATUS_ERROR_EXCEPTION 0x1016at the first AR call, confirming this kernel (not one of the other variants) is what gets dispatched on the SGLang + ATOM + FP8 MoE production path.Performance impact
End-to-end serving benchmark on Qwen3.5-397B-A17B-FP8, TP=4, MI308X × 4, SGLang + ATOM, greedy decode, concurrency 224, ISL=4094, OSL=2048, 448 requests:
All deltas are within run-to-run noise; the with-fix run is marginally faster on most metrics. This matches the expectation that
end_sync<ngpus, true>adds one cross-GPU P2P write plus one spin-load per AR call — exactly the cost every other AR kernel variant in this file already pays.Compatibility
cross_device_reduce_1stageexit timingregistered_input=False, disabling CUDA graphs, or moving AR inputs to long-lived tensors) is needed for correctnessNotes for maintainers
The bug has been latent since
cross_device_reduce_1stagewas introduced; it manifests only when several conditions align: monolithic CUDA-graph capture (not piecewise) + a large model with many AR call sites per captured graph +registered_input=True. Existing vLLM and standalone deployments mostly use piecewise capture and have not been affected.Checklist
csrc/include/custom_all_reduce.cuhcross_device_reduce_1stageexit timing