Repository navigation
Description
Repository: hw-native-sys/simpler
Re-filed after the previous issue was closed. This version isolates the
failure to simpler's own in-tree example (no pypto-lib / model code) and
adds a version-invariance matrix that rules out CANN, simpler, Python, and
PTOAS versions as the cause. Net: the bug tracks theos.fork()+ raw
HcclGetRootInfochip-worker bootstrap, not any dependency version.
Summary
Any multi-rank Worker(level=3) dispatch that reaches _ensure_comm_base
crashes during HCCL base-communicator bootstrap:
- rank 0 (the rootinfo writer) dies inside
comm_init
(simpler/task_interface.py:589→ C++ChipWorker::comm_init→
comm_hccl.cppHcclGetRootInfo) before it writes the rootinfo
handshake file. - every other rank then reports
[ERROR] comm_init: [comm_hccl.cpp:286] [comm rank N] Timeout waiting for rootinfo
afterHCCL_CONNECT_TIMEOUT, because rank 0 never produced the file.
This blocks every multi-rank L3 deployment in our environment.
It reproduces with simpler's own l3/allreduce_distributed example — no
model, no pypto-lib, and the parent process does no NPU/ACL work before
forking the chips.
Minimal reproducer (simpler in-tree example only)
# venv with simpler installed, pto-isa available, >=2 NPUs, ptoas on PATH
export PTO_ISA_ROOT=/path/to/pto-isa
cd /path/to/simpler/examples/workers/l3/allreduce_distributed # in-tree example
PYTHONFAULTHANDLER=1 python -X faulthandler main.py -p a2a3 -d 0-1Observed:
[chip_process pid=14062 dev=0] ready
[chip_process pid=14064 dev=1] ready
Fatal Python error: Segmentation fault
Current thread (most recent call first):
File ".../simpler/task_interface.py", line 589 in comm_init
File ".../simpler/worker.py", line 732 in _handle_ctrl_comm_init
File ".../simpler/worker.py", line 937 in _run_chip_main_loop
File ".../simpler/worker.py", line 999 in _chip_process_loop
File ".../simpler/worker.py", line 1959 in _start_hierarchical
File ".../simpler/worker.py", line 2573 in run
...
File ".../examples/workers/l3/allreduce_distributed/main.py", line 208 in run
[ERROR] comm_init: [comm_hccl.cpp:286] [comm rank 1] Timeout waiting for rootinfo
In main.py, the parent only builds CPU torch tensors and compiles kernels
before worker.init() forks the chips (the base comm is lazy). Verified that
import torch does not load libhccl/libascendcl in the parent
(checked /proc/self/maps) — so the parent is clean at fork time.
Crash locus (rank-0 Ascend plog)
The forked rank-0 chip sets its device and brings up ACL/runtime cleanly, then
dies right at the start of HCCL bring-up — last lines before SIGSEGV:
DRV ... Setup device succeeded. (logical_devid=0; devid=8; ...)
RUNTIME ... StarsEngine: Constructor.
ASCENDCL ... GetAllPackageVersion: Version of hccl package is 9.0.0.beta1
ASCENDCL ... GetAllPackageVersion: Version of driver package is not found <-- last line, then SIGSEGV
No HcclGetRootInfo completion; no rootinfo file written. The crash is inside
the HCCL library call path, in the os.fork()'d child.
Version-invariance matrix (the decisive evidence)
The same minimal repro was run across four independent version axes. Every
combination fails identically (rank 1 Timeout waiting for rootinfo, rank 0
gone):
| CANN | simpler | Python | PTOAS | Result |
|---|---|---|---|---|
| 9.0.0-beta.1 | 6e84154d |
3.10 | v0.43 | ❌ rank-1 rootinfo timeout |
| 9.0.0-beta.1 | afb5c5a9 (latest main) |
3.11 | v0.44 | ❌ |
| 8.5.1 | afb5c5a9 (latest main) |
3.11 | v0.44 | ❌ |
For the 8.5.1 row we confirmed the chip actually loaded the 8.5.1 HCCL (not a
stale 9.0.0 one) via the live process map:
$ grep libhccl /proc/<rank1-chip-pid>/maps
/usr/local/Ascend/cann-8.5.1/x86_64-linux/lib64/libhccl.so
So CANN version, simpler version, Python version, and PTOAS version are all
ruled out. The only invariant is the chip-worker bootstrap: a process created
by os.fork() (worker.py:1935/1955/2002, lazily at Worker.init()) calling
raw HcclGetRootInfo from comm_hccl.cpp.
Control: vLLM (spawn) works on the identical hardware/CANN
On the same pod, same 8 cards, same CANN 9.0.0-beta.1, same driver 25.5.1,
vllm serve <model> -tp 8 (vLLM-Ascend, which uses torch.distributed
init_process_group("hccl") with spawned workers) initializes HCCL across
all 8 ranks and serves cleanly. So the driver / HCCL / hardware stack is
healthy; the differentiator is how the rank process is created — vLLM
spawns fresh processes, simpler forks.
Root-cause hypothesis
HcclGetRootInfo / HCCL bring-up is not safe to call from an os.fork()'d
child under these CANN builds, even when the parent never touched HCCL/ACL
before the fork. vLLM avoids it by spawning fresh worker processes.
Candidate fixes (all upstream / simpler-side):
- Spawn (or
fork+exec) chip workers for the HCCL/comm path instead of
bareos.fork(), so each rank brings up HCCL in a clean process. - Guarantee the chip is forked before any ACL/HCCL/driver state is loaded
in the parent, and that the child performs a complete clean ACL init before
HcclGetRootInfo— then document/verify that ordering invariant. - Provide a distributed L3 ST that exercises
cw.comm_initend-to-end from a
forked chip on CANN 8.5.1 / 9.0.0 + driver 25.5.1, so this is covered in CI.
What we ruled out
- CANN version — fails on both 9.0.0-beta.1 and 8.5.1 (8.5.1 libhccl load verified).
- simpler version — fails on
6e84154dand latest mainafb5c5a9. - Python version — fails on 3.10 and 3.11.
- PTOAS version — fails on v0.43 and v0.44 (PTOAS is not in the comm path).
- Parent pre-initialized HCCL — the allreduce parent does no NPU work before fork;
import torchdoes not load libhccl/libascendcl. - Missing HCCL env —
HCCL_CONNECT_TIMEOUT=3600,HCCL_INTRA_ROCE_ENABLE=1,HCCL_INTRA_PCIE_ENABLE=0,HCCL_OP_EXPANSION_MODE=AIVall set;HCCL_SOCKET_IFNAME/HCCL_IF_BASE_PORToverrides change nothing. - NPU contention — cards are exclusively assigned (private pod); verified free before each run.
Environment
| Item | Value |
|---|---|
| OS | Ubuntu 22.04, x86_64 host + Ascend 910B via PCIe |
| NPU | 8 × Ascend 910B2C |
| Driver | npu-smi 25.5.1 |
| CANN | 9.0.0-beta.1 (also reproduced on 8.5.1) |
| Python | 3.10.12 (also reproduced on 3.11.14) |
| simpler | 6e84154d and latest main afb5c5a9 |
| pto-isa | 109c9f72 |
| ptoas | binary v0.44 (also v0.43) |
Note on a symbolized C++ backtrace
We could not produce a gdb/core backtrace in our pod: gdb is not installed,
ulimit -c is hard-capped at 0, and core_pattern pipes to apport (not
running in-container). The Ascend ATRACE SIGSEGV handler did not leave a
symbolized stack in plog either. Happy to capture one if you can advise a
symbol build / point us at a gdb-enabled image.
Activity
[-][Bug] `ChipWorker.comm_init` SIGSEGV during HCCL bootstrap on first L3 multi-rank dispatch[/-][+][Bug] Multi-rank `comm_init` fails in `os.fork()`'d chip worker — rank 0 dies during HCCL bring-up, version-invariant[/+]sunkaixuan2018 commentedon Jun 10, 2026
我重新看了一下这个 issue,之前标题里怀疑的
os.fork()可能不是根因。现在更像是 HCCL 弱符号没有在运行期解析到,导致 rank0 在
comm_init里调用HcclGetRootInfo时跳到 NULL 地址。链路大概是:
comm_hccl.cpp调用了HcclGetRootInfo/HcclCommInitRootInfo。hccl/hccl_comm.h里,这些 HCCL API 是 weak symbol。host_runtime.so虽然在 CMake 里找到了libhcomm.so,但 x86 Ubuntu/conda gcc 链接时默认可能带--as-needed。--as-needed只会因为 strong undefined reference 保留库,weak undefined reference 不会让libhcomm.so被保留。libhcomm.so没进DT_NEEDED,运行期HcclGetRootInfo解析成 NULL。HcclGetRootInfo就段错,没写 rootinfo,其他 rank 后面就等握手文件超时。这也能解释为什么 x86 会崩、ARM host 不一定崩:CANN 头里弱符号声明两边一样,差别更可能来自构建工具链是否默认启用了
--as-needed。建议修复是在 onboard host runtime 的 CMake 里,强制让
libhcomm.so留在DT_NEEDED中:这里用
--push-state/--pop-state,只影响${HCCL_LINK_TARGETS}这一段,不改变后面其他库的链接状态。可以这样验证:
期望能看到:
如果
libhcomm.so出现在依赖里,HcclGetRootInfo/HcclCommInitRootInfo就会由libhcomm.so提供,不会再因为 weak symbol 未解析成 NULL 而在comm_init段错。Fix: keep HCCL weak-link dependency
Fix: keep HCCL weak-link dependency
fix(host): force libhcomm DT_NEEDED via --no-as-needed (hw-native-sys…
fix(a2a3 comm): unblock multi-rank IPC end-to-end
feat(step3p5): initial in-tree drop of step3p5 decoder + Phase A spli…
fix(host): force libhcomm DT_NEEDED via --no-as-needed (hw-native-sys…
fix(a2a3 comm): unblock multi-rank IPC end-to-end
ChaoWao commentedon Jul 11, 2026
Resolved by #1158 — the weak-link root cause this issue settled on (
libhcommdropped fromDT_NEEDEDunder--as-neededbecause the HCCL entry points are weak symbols in CANN'shccl_comm.h→HcclGetRootInforesolves toNULL→ rank 0 SIGSEGV incomm_initbefore writing the rootinfo handshake) is fixed for both a2a3 and a5 onboard host runtimes (-Wl,--no-as-needed ${HCCL_LINK_TARGETS} -Wl,--as-needed).Verified on a current main build —
libhcomm.sois inDT_NEEDEDof all fourlibhost_runtime.sovariants:#1032 proposed the same fix (
--push-state/--pop-statevariant) and is closed as duplicate. Closing.Fix: use ACL VMM shareable-handle for cross-process comm windows
Fix: use ACL VMM shareable-handle for cross-process comm windows
Fix: use ACL VMM shareable-handle for cross-process comm windows (#1334)
Fix: use ACL VMM shareable-handle for cross-process comm windows (hw-…
feat(step3p5): initial in-tree drop of step3p5 decoder + Phase A spli…
Fix: honor ASCEND_RT_VISIBLE_DEVICES at comm_hccl MemSetAccess sites
Fix: honor ASCEND_RT_VISIBLE_DEVICES at comm_hccl MemSetAccess sites (#…