CANN HCCL-COMM 通信拓扑感知:16卡训练时为什么 rank3 总是最慢的那张
### CANN HCCL-COMM 通信拓扑感知:16卡训练时为什么 rank3 总是最慢的那张
去年搭了一台 8 卡 Atlas 800 服务器做 LLaMA 预训练,一切顺利。后来集群扩到 3 台共 24 卡,单卡吞吐从 1.2 tokens/s 掉到 0.7。不是线性下降,是断崖式下跌。
查了一圈发现不是算力的问题——HCCL 在做跨机 AllReduce 的时候,rank3 到 rank8 的 RoCE 链路走的交换机端口被别的业务占了,实际带宽只有标称的 30%。
HCCL-COMM 是 HCCL 的通信拓扑管理模块。它负责发现 NPU 之间的物理连接关系,构建通信拓扑图,然后基于这个图选择最优的通信路径。你不用手写拓扑,HCCL-COMM 会自动探测。但如果自动探测的结果跟实际物理环境不一致,性能就会出问题。
HCCL-COMM 怎么发现“谁和谁连着”
HCCL-COMM 启动的时候会做一次链路探测:
- 每张 NPU 向其他所有 NPU 发探测包。
- 记录每条链路的类型(HCCS/RoCE)、带宽、延迟。
- 构建一个完整的拓扑图。
- 基于拓扑图选择 AllReduce/AllGather 的最优路径。
拓扑探测示例:
# hccl-comm 的拓扑探测结果fromhccl_commimportTopologyInspector inspector=TopologyInspector()# 查看所有链路links=inspector.get_all_links()forlinkinlinks:print(f"{link.src}→{link.dst}: "f"{link.type},{link.bandwidth}Gbps,{link.latency}us")# 单机8卡输出类似:# rank0 → rank1: HCCS, 100Gbps, 0.8us# rank0 → rank2: HCCS, 100Gbps, 0.9us# rank0 → rank3: HCCS, 100Gbps, 0.8us# ...# rank0 → rank8: RoCE, 100Gbps, 5.2us ← 跨机了# rank0 → rank9: RoCE, 100Gbps, 5.1us单机内:HCCS 的拓扑不是全互联
很多人以为单机 8 卡的 HCCS 是全互联(任意两卡直连)。实际上不是——达芬奇架构的 HCCS 是一种混合拓扑:相邻卡之间有直连链路,不相邻的卡需要中转。
Atlas 800 的典型 HCCS 拓扑是“环形 + 对角”:
rank0 — rank1 — rank2 — rank3 | | | | rank7 — rank6 — rank5 — rank4横向链路是直连的(延迟 <1μs),纵向和对角线需要经过中间卡中转(延迟 1.5-2μs)。HCCL-COMM 探测到这个拓扑之后,在做 Ring AllReduce 时会优先选横向链路构建 Ring,避免走中转。
Ring 顺序优化示例:
# 让 hccl-comm 打印它选择的 ring 顺序fromhccl_commimportTopologyInspector inspector=TopologyInspector()ring=inspector.get_ring_order(num_cards=8)# 理想的 ring:rank0→rank1→rank2→rank3→rank4→rank5→rank6→rank7→rank0# 每一步都是横向直连,没有中转print(ring)# [0, 1, 2, 3, 4, 5, 6, 7]# 如果 hccl-comm 选错了 ring,可能出现中转# 比如选了 0→3→6→1→4→7→2→5→0# rank0 到 rank3 没有直连链路,要经过 rank1 或 rank2 中转# 每步多花 1μs,8步就多 8μs# allreduce 一次多花 64μs,一个 training step 调 20 次 allreduce = 1.28ms# 看起来不多,但累积下来每个 step 多花 5-8% 的时间跨机:RoCE 的拓扑坑更多
跨机通信走 RoCE(RDMA over Converged Ethernet),链路情况比 HCCS 复杂得多。交换机型号、网线类型、网卡驱动版本都会影响实际带宽。
RoCE 链路质量检测:
fromhccl_commimportRoCEInspector inspector=RoCEInspector()# 查看 rank0 到所有其他机器上的 rank 的链路质量forrankinrange(8,24):link=inspector.get_link(0,rank)actual_bw=inspector.measure_bandwidth(0,rank,size_mb=64)util=actual_bw/link.negotiated_bandwidthprint(f"rank0 → rank{rank:2d}: "f"negotiated={link.negotiated_bandwidth}Gbps, "f"actual={actual_bw:.1f}Gbps, "f"utilization={util:.0%}")# rank0 → rank 8: negotiated=100Gbps, actual=97.2Gbps, utilization=97%# rank0 → rank 9: negotiated=100Gbps, actual=95.8Gbps, utilization=96%# rank0 → rank10: negotiated=100Gbps, actual=31.4Gbps, utilization=31% ← 异常!# rank0 → rank11: negotiated=100Gbps, actual=98.1Gbps, utilization=98%rank10 的利用率只有 31%,说明链路有问题。常见原因:
- 交换机端口速率协商失败:本应跑 100Gbps,实际降级到了 25Gbps。
- 网卡 NUMA 绑定错误:RoCE 网卡绑到了错误的 CPU NUMA 节点,跨 NUMA 访存增加延迟。
- 物理连接问题:光模块脏了、光纤弯折角度过大。
快速检查命令:
# 快速检查网卡状态ibstat# 看 rate 一列是不是 Negotiated 100Gbps# 如果是 25Gbps 或 10Gbps,说明协商降级了# 检查 NUMA 绑定lscpu|grepNUMA# 确认 NPU 和 RoCE 网卡在同一个 NUMA 节点上# 如果不在,用 numactl 重新绑定进程手动指定拓扑:自动探测不靠谱的时候
有时候自动探测的结果跟你预期的不一样,比如它以为两条链路都是 HCCS,但实际有一条走了 PCIe 中转。这时候可以手动指定拓扑:
手动拓扑配置文件hccl_topology.json:
{"version":"1.0","devices":[{"rank":0,"device_id":0,"node_id":0},{"rank":1,"device_id":1,"node_id":0},{"rank":2,"device_id":2,"node_id":0},{"rank":3,"device_id":3,"node_id":0},{"rank":4,"device_id":0,"node_id":1},{"rank":5,"device_id":1,"node_id":1},{"rank":6,"device_id":2,"node_id":1},{"rank":7,"device_id":3,"node_id":1}],"links":[{"src":0,"dst":1,"type":"HCCS","bandwidth_gbps":100},{"src":0,"dst":2,"type":"HCCS","bandwidth_gbps":100},{"src":0,"dst":4,"type":"RoCE","bandwidth_gbps":100},{"src":1,"dst":5,"type":"RoCE","bandwidth_gbps":50}]}启动训练时指定拓扑文件:
exportHCCL_TOPOLOGY_FILE=/path/to/hccl_topology.json手动拓扑在两种场景下特别有用:
- 集群网络环境复杂:多种交换机混用。
- 物理环境跟默认配置不一致:比如某个 HCCS 端口坏了降级走了 PCIe。
Hierarchical AllReduce:大规模集群必须用的策略
当集群规模超过 8 卡(2台机器),简单的 Ring AllReduce 不再是最优选择。HCCL-COMM 会自动切换到 Hierarchical 策略:先做单机内的 AllReduce(HCCS),再做跨机的 AllReduce(RoCE)。两层的算法可以分别优化。
Hierarchical 策略配置与性能对比:
fromhccl_commimportHierarchicalConfig# 24卡集群,3台机器各8卡config=HierarchicalConfig(num_nodes=3,cards_per_node=8,intra_node_algorithm='ring',# 机内用 ring,HCCS 快inter_node_algorithm='ring',# 机间也用 ringintra_node_link='HCCS',# 100Gbps,延迟 <1μsinter_node_link='RoCE',# 100Gbps,延迟 ~5μs)# hierarchical allreduce 的执行过程:# 第1阶段:每台机器内部做 ring allreduce,得到局部结果# 8卡 ring,HCCS 链路,32MB 数据 → 0.82ms# 第2阶段:3台机器之间做 allreduce,聚合局部结果# 3节点 ring,RoCE 链路,32MB 数据 → 2.56ms# 第3阶段:每台机器内部再广播跨机结果# 8卡 broadcast,HCCS 链路,32MB 数据 → 0.41ms# 总计:3.79ms# 对比 naive ring allreduce(24卡一个大环):# 24步 ring,混合 HCCS+RoCE 链路 → 6.2ms# hierarchical 快了 40%调优清单
大规模分布式训练遇到通信瓶颈,按这个顺序排查:
# 1. 拓扑是否正确fromhccl_commimportTopologyInspector TopologyInspector().print_topology()# 检查 HCCS/RoCE 分配是否符合物理实际# 2. Ring 顺序是否最优topo=TopologyInspector()print(topo.get_ring_order(num_cards=24))# 确认跨机跳跃最少# 3. 链路利用率是否正常forrankinrange(24):bw=topo.measure_bandwidth(0,rank,size_mb=64)ifbw<80:# 低于 80Gbps 就有问题print(f"⚠ rank0 → rank{rank}: only{bw}Gbps")# 4. hierarchical 是否启用config=topo.get_hierarchical_config()ifconfig.num_nodes>2andnotconfig.enabled:print("⚠ 多机集群没有启用 hierarchical allreduce")HCCL-COMM 是昇腾多卡训练里最容易出问题但最不容易被发现的模块。因为它大部分时候工作正常,只有当集群规模扩大或者网络环境变化时才会暴露问题。建议每次扩容集群之后都跑一遍上面的检查。
仓库地址:https://atomgit.com/cann/hccl
