Platform
a5 (Ascend 950 hardware)
Runtime Variant
tensormap_and_ringbuffer
Description
Worker.close() intermittently raises TimeoutError: child process(es) [<pid>] did not exit within the close budget from the st_worker fixture's teardown. The test body itself always passes — the collective completes and its golden check succeeds — so this is a host-side child-reap problem, not a collective-correctness or inter-card communication problem.
The reap budget is _ROLLBACK_GRACEFUL_TIMEOUT_S = 10.0 s (python/simpler/worker.py:224). After SHUTDOWN is broadcast to every child group, _reap_child_groups polls each pending pid with os.waitpid(WNOHANG) until that deadline, then reports every remaining pid as a survivor.
Evidence that it is a tail-latency effect in child device teardown rather than a hang or a bad card:
- The test body passes. Each failing subprocess reports
1 passed, 1 error, with the error attached to teardown. Across 4 full runs there were 0 golden mismatches and 0 AssertionErrors.
- Usually a single child survives. In 12 observed failures, 10 had exactly 1 surviving child, 1 had 2, and 1 had 3. A global deadlock or a capacity limit would strand children together, not leave one straggler.
- No case, rank count, or device is implicated. 12 failures were spread over 9 distinct cases (4 two-card, 5 four-card). Surviving children appeared on devices 1, 3, 5, 6 and 7 (0 / 2 / 4 never).
TestAllreduceOnephaseP4 run alone (-k OnephaseP4) passes reliably.
- It scales with concurrency, not with card health. An 8-card
--device pool runs 4 device-jobs at once, so up to 16 chip children finalize their CANN device state (reset device, HCCL comm destroy, VMM unmap) simultaneously. The 2-card-only a5 configuration that existed before 8 cards came online essentially never surfaced this.
Distinct from the reap bug fixed in 8e3bebf ("exit a forked worker child once its parent is gone"): that one reproduced on every 8-rank run against a pre-8e3bebf4 install and is gone after reinstalling. This one is intermittent on a current install.
Not yet done, and the decisive next step: capture a backtrace of a surviving pid (/proc/<pid>/stack, or gdb) to identify which CANN call it is sitting in. That is the only way to separate "slower than the 10 s budget" from "genuinely wedged". A --max-parallel 1 run would also confirm or refute the concurrency hypothesis.
Steps to Reproduce
-
On a host with >= 4 a5 NPUs, add "a5" to the platforms list of the 4-rank collective cases under tests/st/worker/collectives/ (as of 22f2d53 they list only ["a2a3sim", "a2a3", "a5sim"]).
-
Run the collectives suite against a multi-card pool so several device-jobs run concurrently:
task-submit --timeout 2400 --max-time 2300 --device 0,1,2,3,4,5,6,7 \
--run "source /usr/local/Ascend/cann-9.2.0/set_env.sh && source .venv/bin/activate && \
export LD_PRELOAD=/usr/lib64/libstdc++.so.6 && \
python -m pytest tests/st/worker/collectives --platform a5 --device 0-7 \
-v --require-pto-isa --pto-session-timeout 600 -p no:cacheprovider"
-
Repeat several times. Observed failure counts over 4 consecutive 18-case runs: 1, 2, 5, 4. An earlier run was fully green (18/18), so a single passing run does not clear it.
Note: LD_PRELOAD is needed on this host because the venv interpreter carries a DT_RPATH of $ORIGIN/../lib pointing at an anaconda libstdc++ that lacks GLIBCXX_3.4.30; RPATH is consulted before LD_LIBRARY_PATH, so setting LD_LIBRARY_PATH does not help. Unrelated to this bug, but required to get the suite to run.
Expected Behavior
Worker.close() reclaims every fork child and returns cleanly, so teardown does not fail a test whose body passed.
Actual Behavior
tests/st/worker/collectives/allreduce/test_allreduce.py .E [100%]
==================================== ERRORS ====================================
________ ERROR at teardown of TestAllreduceBidirectionalRingP4.test_run ________
...
if survivors:
errors.append(TimeoutError(f"child process(es) {survivors} did not exit within the close budget"))
for msg in bad_exits:
errors.append(RuntimeError(f"child teardown: {msg}"))
if errors:
> raise errors[0]
E TimeoutError: child process(es) [576888] did not exit within the close budget
python/simpler/worker.py:6341: TimeoutError
---------------------------- Captured stderr setup -----------------------------
[chip_process pid=576887 dev=4] ready
Cases observed failing this way, over 4 runs (12 failures / 9 distinct cases):
| Case |
Ranks |
Hits |
TestAllreduceTwophaseP4 |
4 |
2 |
TestAllreduceIbingP2 |
2 |
2 |
TestAllreduceBidirectionalRingP4 |
4 |
2 |
TestBroadcastP4 |
4 |
1 |
TestAllToAllP4 |
4 |
1 |
TestAllreduceRingP4 |
4 |
1 |
TestAllreduceOnephaseP4 |
4 |
1 |
TestAllreduceOnephaseP2 |
2 |
1 |
TestAllgatherP2 |
2 |
1 |
Git Commit ID
22f2d53
CANN Version
9.2.0
Driver Version
npu-smi 25.6.rc1.b188
Host Platform
Linux (aarch64)
Additional Context
Location: python/simpler/worker.py:6341 (_reap_child_groups, raising after its bounded poll), reached via close() -> _teardown_ready_tree(). Surfaces through the st_worker fixture in conftest.py.
Impact: this reddens a multi-card a5 onboard run even though every collective is functionally correct. Verified separately that 8-rank allreduce (examples/workers/l3/allreduce/main.py -p a5 -d 0-7) matches golden on all 8 ranks, and that all 18 collective cases pass on a5 across 2- and 4-rank configurations.
Platform
a5 (Ascend 950 hardware)
Runtime Variant
tensormap_and_ringbuffer
Description
Worker.close()intermittently raisesTimeoutError: child process(es) [<pid>] did not exit within the close budgetfrom thest_workerfixture's teardown. The test body itself always passes — the collective completes and its golden check succeeds — so this is a host-side child-reap problem, not a collective-correctness or inter-card communication problem.The reap budget is
_ROLLBACK_GRACEFUL_TIMEOUT_S = 10.0s (python/simpler/worker.py:224). AfterSHUTDOWNis broadcast to every child group,_reap_child_groupspolls each pending pid withos.waitpid(WNOHANG)until that deadline, then reports every remaining pid as a survivor.Evidence that it is a tail-latency effect in child device teardown rather than a hang or a bad card:
1 passed, 1 error, with the error attached to teardown. Across 4 full runs there were 0 golden mismatches and 0 AssertionErrors.TestAllreduceOnephaseP4run alone (-k OnephaseP4) passes reliably.--devicepool runs 4 device-jobs at once, so up to 16 chip children finalize their CANN device state (reset device, HCCL comm destroy, VMM unmap) simultaneously. The 2-card-only a5 configuration that existed before 8 cards came online essentially never surfaced this.Distinct from the reap bug fixed in 8e3bebf ("exit a forked worker child once its parent is gone"): that one reproduced on every 8-rank run against a pre-8e3bebf4 install and is gone after reinstalling. This one is intermittent on a current install.
Not yet done, and the decisive next step: capture a backtrace of a surviving pid (
/proc/<pid>/stack, or gdb) to identify which CANN call it is sitting in. That is the only way to separate "slower than the 10 s budget" from "genuinely wedged". A--max-parallel 1run would also confirm or refute the concurrency hypothesis.Steps to Reproduce
On a host with >= 4 a5 NPUs, add
"a5"to theplatformslist of the 4-rank collective cases undertests/st/worker/collectives/(as of 22f2d53 they list only["a2a3sim", "a2a3", "a5sim"]).Run the collectives suite against a multi-card pool so several device-jobs run concurrently:
Repeat several times. Observed failure counts over 4 consecutive 18-case runs: 1, 2, 5, 4. An earlier run was fully green (18/18), so a single passing run does not clear it.
Note:
LD_PRELOADis needed on this host because the venv interpreter carries aDT_RPATHof$ORIGIN/../libpointing at an anacondalibstdc++that lacksGLIBCXX_3.4.30; RPATH is consulted beforeLD_LIBRARY_PATH, so settingLD_LIBRARY_PATHdoes not help. Unrelated to this bug, but required to get the suite to run.Expected Behavior
Worker.close()reclaims every fork child and returns cleanly, so teardown does not fail a test whose body passed.Actual Behavior
Cases observed failing this way, over 4 runs (12 failures / 9 distinct cases):
TestAllreduceTwophaseP4TestAllreduceIbingP2TestAllreduceBidirectionalRingP4TestBroadcastP4TestAllToAllP4TestAllreduceRingP4TestAllreduceOnephaseP4TestAllreduceOnephaseP2TestAllgatherP2Git Commit ID
22f2d53
CANN Version
9.2.0
Driver Version
npu-smi 25.6.rc1.b188
Host Platform
Linux (aarch64)
Additional Context
Location:
python/simpler/worker.py:6341(_reap_child_groups, raising after its bounded poll), reached viaclose()->_teardown_ready_tree(). Surfaces through thest_workerfixture inconftest.py.Impact: this reddens a multi-card a5 onboard run even though every collective is functionally correct. Verified separately that 8-rank allreduce (
examples/workers/l3/allreduce/main.py -p a5 -d 0-7) matches golden on all 8 ranks, and that all 18 collective cases pass on a5 across 2- and 4-rank configurations.