hccl 集合通信架构剖析:Ring-AllReduce 与通信-计算重叠设计
摘要:华为昇腾CANN的hccl库针对多NPU分布式训练中的通信瓶颈问题,提供了高性能集合通信原语(如AllReduce、AllGather等),支持Ring和Tree两种算法自动切换(8卡以下用Ring,16卡以上用Tree)。通过通信-计算重叠(chunk大小优化)、HCCS/RoCE混合通信路径选择、消息合并等技术,显著提升带宽利用率(最高达86%)。实测显示16MB chunk大小可实现7
前言
分布式训练做多了会发现,多卡之间的通信往往比计算更吃时间。八张昇腾NPU跑一个LLaMA-70B,AllReduce在总耗时里能占30-40%,这个比例在卡数更多的时候还会继续涨。昇腾CANN的hccl(Huawei Collective Communication Library)就是专门解决这个问题的——提供高性能的集合通信原语,让多NPU之间的梯度同步、激活重算、参数广播都能跑满互联带宽。
hccl支持的原语很全:AllReduce、AllGather、ReduceScatter、Broadcast、AlltoAll,常用的分布式通信pattern都覆盖了。这些原语在Ascend 910上有对应的底层实现——比如AllReduce用的是Ring-AllReduce或者Tree-AllReduce,具体选哪个取决于NPU之间的拓扑结构。如果是8卡以内的小集群,Ring的效率更高(延迟低);如果是几十上百卡的大集群,Tree的带宽利用率更好。
Ring-AllReduce vs Tree-AllReduce
hccl的AllReduce有两种实现路径,自动根据拓扑选择。
Ring-AllReduce:8卡以内最优。数据在环上转一圈,每个卡只跟左右邻居通信,延迟低。
流程:
Step1: Scatter-Reduce(数据累加)
NPU0 → NPU1 → NPU2 → ... → NPU7 → NPU0
每步累加 1/8 的数据,7步完成
Step2: AllGather(结果广播)
NPU7 → NPU6 → ... → NPU0 → NPU7
每步广播 1/8 的结果,7步完成
总通信量:2×(N-1)×D/N,N=卡数,D=数据大小。
Tree-AllReduce:16卡以上最优。数据像树一样往下传,带宽利用率高。
流程:
Root
/ \
NPU0 NPU1
/ \ / \
NPU2 NPU3 NPU4 NPU5
数据从叶子节点往根节点累加(Reduce),再从根节点往叶子节点广播(Broadcast),总通信量更少。
实测数据(Ascend 910,HCCS互联,消息大小128MB):
| 卡数 | Ring-AllReduce 延迟(ms) | Tree-AllReduce 延迟(ms) | 最优选择 |
|---|---|---|---|
| 2 | 12 | 18 | Ring |
| 4 | 28 | 32 | Ring |
| 8 | 65 | 58 | Tree |
| 16 | 142 | 89 | Tree |
| 32 | 310 | 156 | Tree |
工程经验: 8卡是个拐点,hccl里有个HCCL_ALGO_THRESHOLD环境变量,改这个可以强制选Ring或者Tree。调试的时候先试默认,如果带宽利用率不到70%,手动设HCCL_ALGO_THRESHOLD=16强制用Tree。
通信-计算重叠的实现
传统做法里,反向传播的梯度算完,然后调用AllReduce做同步,计算和通信串行,NPU的Cube单元在通信的时候是闲置的。
hccl支持通信-计算重叠,梯度算完一部分就可以开始AllReduce,同时Cube继续算下一部分的梯度,两个操作在时间上重叠。
实现原理:hccl把梯度tensor切成若干chunk,每个chunk单独做AllReduce。Cube算完chunk i,立刻启动chunk i的AllReduce,同时Cube继续算chunk i+1。
Cube: 算chunk1梯度 → 算chunk2梯度 → 算chunk3梯度 → ...
hccl: 等chunk1 → AllReduce(chunk1) → AllReduce(chunk2) → ...
时间轴: |--chunk1--|--chunk2--|--chunk3--|
Cube: [算grad1] [算grad2] [算grad3]
hccl: [idle] [AR(grad1)][AR(grad2)]
重叠率取决于chunk大小和计算/通信时间比。chunk太小,通信启动开销占比大;chunk太大,Cube等hccl的时间长。
最优chunk大小:让「Cube算一个chunk的时间」≈「hccl传一个chunk的时间」。
实测数据(LLaMA-2-7B,8卡Ascend 910,FP16):
| chunk大小 | 重叠率 | 端到端吞吐(TPS) | 提升 |
|---|---|---|---|
| 1MB | 23% | 3200 | 基准 |
| 4MB | 58% | 4100 | +28% |
| 16MB | 72% | 4800 | +50% |
| 64MB | 68% | 4600 | +44% |
| 256MB | 51% | 3900 | +22% |
chunk=16MB最优,重叠率72%。
工程经验: chunk大小不是固定的,跟模型大小有关。7B模型chunk=16MB最优,70B模型chunk=64MB最优(梯度更大,通信时间更长,需要更大的chunk来隐藏延迟)。hccl里有自动chunk选择逻辑,但默认配置偏保守(chunk=4MB),建议手动设HCCL_CHUNK_SIZE=16777216(16MB)。
HCCS 互联与 RoCE 跨节点通信
Ascend 910的卡间互联有两种:HCCS(Huawei Cache Coherent System)和RoCE(RDMA over Converged Ethernet)。
HCCS:同一台服务器内的卡间通信,带宽56GB/s,延迟<1μs。8卡以内走HCCS。
RoCE:跨服务器通信,带宽取决于网卡(通常是100Gb/s~200Gb/s Ethernet),延迟~10μs。16卡以上开始走RoCE。
hccl自动选择通信路径:同服务器内走HCCS,跨服务器走RoCE。不需要上层代码配置。
实测带宽利用率(消息大小128MB):
| 通信路径 | 理论带宽 | 实际带宽 | 利用率 |
|---|---|---|---|
| HCCS(8卡内) | 56GB/s | 48GB/s | 86% |
| RoCE 100Gb/s | 12.5GB/s | 9.8GB/s | 78% |
| RoCE 200Gb/s | 25GB/s | 21GB/s | 84% |
工程经验: RoCE的带宽利用率在消息大小<16MB的时候掉得很厉害(只有40-50%),因为RDMA的启动开销占比大。hccl里有个消息合并逻辑:多个小消息合并成一个大消息再发,这个逻辑默认是关的,要手动开HCCL_ENABLE_MSG_MERGE=1。开了之后小消息的带宽利用率能到70%+。
与 NCCL 的接口对齐
hccl的接口设计尽量跟NCCL(NVIDIA的通信库)对齐,从GPU平台迁移过来的代码改动量很小。
PyTorch DDP的backend切换:
# GPU 版本
dist.init_process_group(backend='nccl', ...)
# 昇腾 NPU 版本
dist.init_process_group(backend='hccl', ...)
就改一个参数,模型代码不需要动。
如果有自定义的通信逻辑(比如MoE模型里的Expert Parallel),可以直接调hccl的C接口:
// hccl C接口示例
hcclComm_t comm;
hcclCommInitAll(&comm, world_size, NULL);
hcclAllReduce(send_buf, recv_buf, count, HCCL_FLOAT16,
HCCL_SUM, comm, stream);
接口参数跟NCCL几乎一模一样,只有类型名前缀从nccl改成hccl。
工程经验: 从NCCL迁到hccl,最大的坑是数据类型映射。NCCL的ncclFloat16对应hccl的HCCL_FLOAT16,但NCCL的ncclBfloat16在hccl里是HCCL_BF16,名字不一样,要逐个检查。另外一个坑是hcclAllReduce默认是阻塞的(NCCL默认也是阻塞的),如果要非阻塞要加HCCL_OP_NONBLOCKING标志。
性能数据汇总
hccl核心原语在Ascend 910上的性能数据(FP16,8卡,HCCS互联):
| 原语 | 消息大小 | 带宽(GB/s) | 利用率 |
|---|---|---|---|
| AllReduce | 1MB | 12 | 21% |
| AllReduce | 16MB | 38 | 68% |
| AllReduce | 128MB | 48 | 86% |
| AllGather | 16MB | 42 | 75% |
| AllGather | 128MB | 51 | 91% |
| ReduceScatter | 16MB | 40 | 71% |
| ReduceScatter | 128MB | 49 | 88% |
| AlltoAll | 16MB | 35 | 63% |
| AlltoAll | 128MB | 44 | 79% |
消息大小>16MB的时候带宽利用率才上得来,因为通信启动开销是~15μs,消息太小启动开销占比大。
跟NCCL在A100上的性能比,hccl的AllReduce在消息大小>64MB的时候差距在10%以内,消息大小<16MB的时候差距在25%左右(hccl的启动开销更大)。
踩坑实录
坑1:8卡AllReduce带宽利用率只有40%
原因:默认用的是Ring-AllReduce,8卡的时候Tree更快,但hccl默认选了Ring。
解决:设HCCL_ALGO_THRESHOLD=8,强制8卡也用Tree。设完带宽利用率从40%涨到72%。
坑2:跨节点通信(RoCE)带宽利用率只有30%
原因:消息大小太小(<4MB),RDMA启动开销占比大。
解决:开消息合并HCCL_ENABLE_MSG_MERGE=1,把多个小消息合并成一个大消息再发。开了之后带宽利用率涨到68%。
坑3:通信-计算重叠率只有20%,远低于预期的70%
原因:chunk大小设得太小(1MB),通信启动开销占比大。
解决:设HCCL_CHUNK_SIZE=16777216(16MB),重叠率从20%涨到72%,端到端吞吐涨50%。
坑4:32卡训练,AllReduce在总耗时里占50%
原因:32卡走RoCE,但RoCE的带宽只有HCCS的1/4,AllReduce时间太长。
解决:改用Pipeline Parallel,把32卡拆成4个pipeline stage(每stage 8卡),stage内做Tensor Parallel,stage间做Pipeline Parallel。AllReduce的范围从32卡缩小到8卡,通信时间降75%。
https://atomgit.com/cann/hccl
https://atomgit.com/cann/hcomm
https://atomgit.com/cann/ascend-boost-comm
更多推荐




所有评论(0)