Wait for all async all-gathers in coalesced ZeRO-3 parameter gather - #8539
Conversation
_allgather_params_coalesced() launched one async all_gather per parameter but only waited on the last handle before pointing param.data at the torch.empty flat buffers. On CUDA the trailing torch.cuda.synchronize() and same-stream ordering make that safe. On gloo each async handle is independent and the CPU accelerator's synchronize() is a no-op, so the earlier gathers could still be in flight when the gathered parameters were read, exposing uninitialized memory (NaN / denormal garbage) through param.data. The corruption is timing-dependent: local reproduction of tests/unit/v1/zero/test_zero_user_backward.py::TestUnmanagedGradientAccumulation::test_unmanaged_varying_backward_count[3] failed 9/20 runs without this change and 0/20 with it (after deepspeedai#8382's comparison-helper fix made the numeric assertion reachable). Signed-off-by: Guokai Ma <guokai.ma@intel.com> Signed-off-by: Ma, Guokai <guokai.ma@intel.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe58c7ef6d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for handle in launch_handles: | ||
| handle.wait() |
There was a problem hiding this comment.
Add the required Signed-off-by trailer
This is a non-merge commit, but its commit message contains no Signed-off-by trailer, violating the repository's mandatory commit requirement. Recreate the commit with git commit --signoff before merging.
AGENTS.md reference: AGENTS.md:L8-L8
Useful? React with 👍 / 👎.
e71b8f3
Problem
While investigating multi-rank CPU CI (#8381),
TestUnmanagedGradientAccumulation::test_unmanaged_varying_backward_count[3]failed intermittently with parameters turned to NaN, andtest_zero_coalesce_grad_reductionshowed mismatches with denormal garbage values (4.8e+37,1e-38) — the signature of uninitialized memory.Root cause
_allgather_params_coalesced()launches one async all_gather per parameter, but only waits on the last handle before pointingparam.dataat thetorch.emptyflat buffers:On CUDA this is safe by construction: the ops are enqueued from a single thread onto the same stream (FIFO), and
torch.cuda.synchronize()waits for everything anyway. On gloo each async handle runs on an independent background thread with no ordering between handles — waiting for the last one says nothing about the earlier ones — and the CPU accelerator'ssynchronize()is a no-op. Readingparam.datacan therefore race the gather and expose uninitialized memory.The corruption is timing-dependent (locally 9/20 runs fail; #8382's comparison-helper fix made the numeric assertions reachable), which is why it was never caught on GPU CI.
Fix
Wait on every handle. On CUDA the earlier handles are guaranteed complete by stream ordering, so the extra waits are immediate status checks with no real waiting; the change makes correctness independent of that structural coincidence.
Verification (CPU/gloo, world sizes 2–3)
test_unmanaged_varying_backward_count[3]TestZero3ParamPartitioningBase/TestGradientAllreduceOp/TestUnmanagedGradientAccumulation(regression, 57 cases)GPU behavior is unchanged by construction (same buckets, same order, stream-FIFO + full synchronize); GPU CI will re-confirm.