Skip to content

[release/2.14][c10d][nccl2] Fix ROCm timeout-test hangs by splitting the subgroup from the default group - #3946

Open
albmalamd wants to merge 1 commit into
release/2.14from
albmalamd/fix-nccl2-timeout-tests-split-subgroup-release-2.14
Open

albmalamd wants to merge 1 commit into
release/2.14from
albmalamd/fix-nccl2-timeout-tests-split-subgroup-release-2.14

Conversation

@albmalamd

Copy link
Copy Markdown

Linked issue or supporting maintainer

Supported by @pragupta

BC-breaking?

No

Summary (human written only)

This change fixes hangs of the tests test_wait_times_out_and_process_survives and test_timeout_without_process_abort in test_c10d_nccl2.py on release/2.14 with ROCm 10.2. The hang comes from RCCL, and 2.14 hits it because of nccl2's lazy initialization: new_group creates the subgroup as a standalone 8-rank communicator, and for such a communicator RCCL in ROCm 10.2 runs small all_reduces with its DDA kernel, which doesn't check abortFlag. As a result, after the deliberate timeout ncclCommAbort never returns, because RCCL's proxy thread waits in hipDeviceSynchronize for that kernel to finish. The change in _ProcessGroupNCCL2SubgroupTest._init_pg binds the default group to a device (init_process_group(device_id=...)), so the subgroup is created with ncclCommSplit, as on 2.15, and RCCL doesn't use DDA for split communicators, so the abort works. This works around the RCCL bug rather than fixing it; the bug remains for standalone 8-rank communicators until ROCm/rocm-systems#11245 ships.

Agent details (if applicable)

Authored with assistance from an AI coding assistant (Cursor).

Failures. distributed/test_c10d_nccl2.py::ProcessGroupNCCL2BlockingWaitTest::test_wait_times_out_and_process_survives and ::ProcessGroupNCCL2WatchdogNoTearDownTest::test_timeout_without_process_abort hang, F F F at about 30 min per attempt, in ROCm/rockrel run 41069793579. test_c10d_nccl2 took 181 min, and the distributed shard 2/3 job was cancelled at its 6 h limit, so the rest of that shard never ran.

Root cause. release/2.14 initializes nccl2 lazily, so new_group creates the tests' subgroup as a standalone communicator instead of splitting the default one. For a standalone single-node 8-rank communicator, RCCL in ROCm 10.2 runs small all_reduces with its DDA-IPC kernel, which never polls abortFlag. After the deliberate timeout, ncclCommAbort never returns: commReclaim joins the RCCL proxy thread, which is blocked in hipDeviceSynchronize behind that kernel. Split communicators get no DDA resources, so their kernels exit on abort. release/2.15 passes because nccl2 binds the default group eagerly there (pytorch#192895). The RCCL fix is ROCm/rocm-systems#11245 (on develop, not in ROCm 10.2).

Fix. Test-only, +21 lines. _ProcessGroupNCCL2SubgroupTest._init_pg binds the default group to cuda:rank with init_process_group(device_id=...), the pattern ProcessGroupNCCL2EagerNewGroupTest already uses. The subgroup is then split, as on 2.15, and the tests still exercise the same nccl2 timeout, error and abort paths.

Validation (8x MI300X, ROCm 10.2.0rc1, the CI torch 2.14 wheel)

  • The two tests: 4/4 runs hang before the change, 12/12 pass after (about 82 s per pair).
  • Full file after the change: 42 passed, 3 skipped. ProcessGroupNCCLLegacyNonblockingTest is deselected because of a separate race ([c10d] Serialize ncclCommGetAsyncError between the watchdog and helper polls pytorch/pytorch#203369).
  • Causality on the unchanged tests: both pass with RCCL_DDA_ENABLE=0 and with 4 ranks.
  • An RCCL-only repro (no PyTorch) hangs ncclCommAbort on a standalone 8-rank communicator for more than 120 s; on a split communicator it returns in 0.64 s.

Not included. #3700 skips test_timeout_with_communicator_cleanup on ROCm for the same RCCL defect. With this change it passes (2/2), while the baseline hangs, so the skip can be dropped in a follow-up.

…the subgroup from the default group

On release/2.14, test_wait_times_out_and_process_survives and
test_timeout_without_process_abort hang on 8x MI300X (ROCm 10.2) until CI
kills them after 30 minutes, three times each. In ROCm/rockrel run
41069793579, test_c10d_nccl2 took 181 minutes and pushed the distributed shard
past its 6 h limit.

Root cause: the tests time out an all_reduce on a throwaway subgroup and then
abort it. release/2.14 initializes nccl2 lazily, so the default group has no
bound device, and new_group creates the subgroup as a standalone communicator
instead of splitting the default one. For a standalone single-node 8-rank
communicator, RCCL in ROCm 10.2 runs small all_reduces with its DDA-IPC kernel,
which never polls abortFlag. ncclCommAbort then never returns: commReclaim
joins the RCCL proxy thread, which is blocked in hipDeviceSynchronize behind
that kernel. Split communicators never get DDA resources, so their kernels exit
on abort. release/2.15 passes because nccl2 binds the default group eagerly
there (pytorch#192895), so the subgroup is split. Forcing a standalone
subgroup on the 2.15 wheel (lazy_init=True) hangs the same way.

Fix: bind the default group to a device in
_ProcessGroupNCCL2SubgroupTest._init_pg with init_process_group(device_id=...),
as ProcessGroupNCCL2EagerNewGroupTest already does. _new_subgroup then splits
it, as on release/2.15. The tests still cover the same nccl2 timeout, error and
abort paths. The RCCL defect remains for standalone 8-rank communicators until
ROCm/rocm-systems#11245 (merged to develop, not in ROCm 10.2) ships.

Alternatives considered: skipping the tests on ROCm, as #3700 did for
test_timeout_with_communicator_cleanup, drops their coverage. Backporting eager
nccl2 initialization (pytorch#192854, pytorch#192855, pytorch#192895, pytorch#193237)
applies cleanly, but it is a feature backport with C++ changes that makes
every nccl2 group create its communicator eagerly.

Test Plan:

8x MI300X, ROCm 10.2.0rc1 wheels (RCCL 2.32.3), torch 2.14.0+rocm10.2.0rc1
(386e979, the CI wheel):

```
python -m pytest test/distributed/test_c10d_nccl2.py -v -k "test_wait_times_out_and_process_survives or test_timeout_without_process_abort"
```

Before: 4/4 runs hang, with rank 0 in ncclCommAbort and GPU 0 at 100%.
After: 12/12 pass, about 82 s for the pair.

```
python -m pytest test/distributed/test_c10d_nccl2.py -v -k "not ProcessGroupNCCLLegacyNonblockingTest"
```

After: 42 passed, 3 skipped (238 s). ProcessGroupNCCLLegacyNonblockingTest is
deselected because of a separate known race (pytorch#203369).

```
lintrunner -a
```

This PR was authored with the help of an AI assistant.

Co-authored-by: Cursor <cursoragent@cursor.com>
@albmalamd
albmalamd requested a review from pragupta November 5, 2026 15:22
@albmalamd albmalamd changed the title [release/2.14][c10d][nccl2] Fix ROCm timeout-test hangs by splitting … [release/2.14][c10d][nccl2] Fix ROCm timeout-test hangs by splitting the subgroup from the default group Nov 5, 2026

This branch has not been deployed

No deployments
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.

1 participant