HCCL 集合通信:昇腾集群的参数同步引擎
大模型训练依赖高效的分布式通信库实现多卡协同计算。HCCL作为华为昇腾平台的集合通信库,采用Ring AllReduce算法将通信复杂度从O(N²)降至O(N),支持AllReduce等核心操作。其通过拓扑感知优化通信路径,实现计算与通信重叠,并集成到PyTorch框架中。相比NCCL,HCCL针对昇腾硬件特性优化,在大数据量下可达对标硬件的88%带宽利用率。开发者只需切换backend即可在代码
大模型训练的本质是将一个超大矩阵乘法拆到多张 NPU 上并行计算,每张卡算完自己的分片后把梯度合并。合并操作就是集合通信。
HCCL(Huawei Collective Communication Library)是 CANN 的集合通信库,对应 NVIDIA NCCL。它不参与模型计算,不管理训练循环,只做一件事——在多卡之间高效搬运数据。
为什么大模型训练离不开通信
以 LLaMA-70B 为例。单张 Ascend 910 显存 32GB,模型参数 140GB(FP16),放不下。必须把模型切到 8 张卡上,每张卡负责八分之一的参数。
前向计算时每张卡算出自己那部分的梯度,然后需要把所有卡的梯度加起来,每张卡才能更新自己的参数。这个"加起来"就是 AllReduce 操作。
没有通信库的帮助,AllReduce 需要手动写一个算法——每张卡把自己的梯度发给某个收集者,收集者相加完后广播回去。通信量 O(N²)(N 为卡数),卡越多通信效率越低。HCCL 用 Ring AllReduce 把通信量降到 O(N)。
Ring 通信为什么常见
Ring AllReduce 是所有集合通信库的标配算法。以 4 张卡为例:
卡 0 → 卡 1 → 卡 2 → 卡 3 → 卡 0(逻辑 Ring)
每张卡只跟前后邻居通信。梯度被切成 N 块(N=卡数),每张卡持有不同块的数据。
Reduce-Scatter 阶段(4 步):
Step 1: 卡0把块0发给卡1,卡1把块1发给卡2,卡2把块2发给卡3,卡3把块3发给卡0
Step 2: 每张卡收到后做元素求和,再把结果传给下一张
Step 3: 继续传递和累加
Step 4: 每张卡持有某个块的完整和
AllGather 阶段(3 步):
每张卡把自己持有的完整块广播给所有卡,所有卡最终持有全部块的完整和
总通信量 = 2 × (N-1) × (gradient_size / N),跟卡数 N 无关。N 越大,每张卡承担的通信量反而越小。这就是 Ring 算法扩展性的来源。
HCCL 与 NCCL 的区别
拓扑感知不同。 NCCL 为 NVIDIA 的 NVLink 和 InfiniBand 拓扑做了深度优化——知道 GPU 之间是 NVLink 直连还是走 PCIe 交换机,通信路径选择不同。HCCL 针对的是昇腾集群的 HCCS(Huawei Cache Coherence System)互联和 RoCE 网络,拓扑结构自然不同。HCCL 也会自动检测卡间拓扑,但检测方式和通信路径算法跟 NCCL 不一样。
通信原语实现不同。 NCCL 有 150+ 种算法变体(Ring、Tree、NVLink Direct 等),HCCL 的算法数量少一些但覆盖了核心场景:AllReduce、AllGather、ReduceScatter、Broadcast、AllToAll。
集成方式不同。 NCCL 通过 PyTorch 的 DistributedDataParallel 和 DeepSpeed 集成。HCCL 同样集成进了 PyTorch(通过 torch_npu),训练脚本里的 torch.distributed.all_reduce 在昇腾上走的就是 HCCL。应用层代码完全一致——换卡不换代码。
昇腾集群如何同步参数
一条典型的分布式训练命令:
torchrun --nproc_per_node=8 train.py
PyTorch 内部为每张卡创建独立的进程,每个进程绑定一张 NPU。DDP 初始化时调用 torch.distributed.init_process_group(backend="hccl"),HCCL 在背后做的事情:
-
通信域初始化。 8 个进程互相发现,建立通信拓扑。HCCL 自动检测 NPU 之间的物理拓扑(HCCS 直连、PCIe 交换机、RoCE 网络),为每对卡选择最短通信路径。
-
Buffer 注册。 每张卡在显存中分配通信 Buffer,HCCL 把 Buffer 地址注册到 DMA 引擎。注册完成后通信走硬件 DMA,不经过 CPU 拷贝。
-
通信就绪。 所有卡之间建立好通信链路后,DDP 开始训练循环。
训练每步的反向传播结束后,PyTorch 调用 all_reduce(gradients),HCCL 执行 Ring AllReduce,所有卡的梯度同步同值。
HCCL 在 CANN 中的位置
CANN 跟通信相关有三个仓库:
hccl — 集合通信库(AllReduce、AllGather 等)
hcom — 通信算子库(更底层的通信原语)
hixl — 单边通信库(零拷贝场景)
hccl 在最高层,直接对接 PyTorch DDP 和 DeepSpeed。hcom 是 hccl 的下层——hccl 的 AllReduce 调用 hcom 的发送和接收算子。hixl 是另一条路径,用于推理场景的 PD 分离通信。
大模型训练中的通信优化
实际分布式训练中,通信开销不只是一个算法问题,还涉及工程层面的各种权衡。
计算通信重叠。 Ring AllReduce 的 Reduce-Scatter 阶段可以在反向传播的同时异步启动——模型最后一层的梯度刚算出来,通信就开始了,不用等到所有层都算完。这种重叠可以让通信开销"消失"在计算的时间里。HCCL 支持通过 hccl_stream 让通信和计算使用不同的 Stream,实现异步重叠。
梯度压缩。 通信的数据量跟模型大小成正比。LLaMA-70B 每步更新要同步约 140GB 的梯度。FP16 下即便用 Ring 拆分,每张卡每步仍要传输约 35GB。
常见压缩手段:
- FP16 → FP32 混精度:梯度用 FP16 通信,通信量减半
- 梯度裁剪:只同步大于阈值的梯度元素
- TopK 稀疏化:只同步梯度值最大的 K% 元素
前两种 HCCL 原生支持。TopK 稀疏化需要训练框架配合——DeepSpeed 和 Megatron-LM 有自己的稀疏通信实现。
性能参考
在 8×Ascend 910 集群上,HCCL AllReduce 的实测带宽:
| 数据量 | NCCL (A100×8 NVLink) | HCCL (Ascend 910×8 HCCS) |
|---|---|---|
| 1MB | 12 GB/s | 9 GB/s |
| 16MB | 58 GB/s | 42 GB/s |
| 128MB | 92 GB/s | 78 GB/s |
| 512MB | 108 GB/s | 95 GB/s |
HCCL 在大数据量下的带宽能达到对标硬件的 88%(95/108)。小数据量差距大一些(75%),主要原因是 HCCL 的 Ring 算法在小 Buffer 场景的初始化开销偏高。
通信拓扑与带宽利用率
HCCL 的另一个关键优化是拓扑感知的通信路径选择。在一个 8 卡节点上,实际的物理拓扑可能是:
NPU 0 ── HCCS ── NPU 1 NPU 4 ── HCCS ── NPU 5
│ │ │ │
PCIe PCIe PCIe PCIe
│ │ │ │
NPU 2 ── HCCS ── NPU 3 NPU 6 ── HCCS ── NPU 7
NPU 0 和 NPU 1 之间是 HCCS 直连,带宽 100GB/s。NPU 0 和 NPU 2 之间走 PCIe 交换机,带宽只有 32GB/s。HCCL 在通信域初始化阶段通过探测每对卡之间的延迟和带宽,构建一张拓扑权重图。AllReduce 的 Ring 算法在切分数据块时,会优先让数据沿高带宽路径流动,尽量避免通过低带宽路径传输大量数据。
跨节点场景(16+ 卡)中,节点间的网络通信带宽(RoCE 200GbE)远低于节点内的 HCCS 带宽,HCCL 会减少跨节点通信的频次——每轮 AllReduce 只做一次跨节点数据交换。
HCCL 在 PyTorch 训练中的实际调用
实际开发中你大概率不会直接调用 HCCL 的 API。PyTorch 的 torch.distributed 封装了 HCCL,你只需要在初始化时指定 backend:
import torch.distributed as dist
dist.init_process_group(backend="hccl") # 后端换成 hccl
后面的 dist.all_reduce(tensor)、dist.broadcast(tensor, src=0) 都走 HCCL。从应用代码的视角看,NCCL 和 HCCL 的切换就是改一个 backend 名字。
HCCL 也提供 C API 供自定义通信场景使用——比如在 GNN 训练中手动控制通信粒度。C API 的接口跟 NCCL 的 nccl.h 非常相似,方便从 GPU 迁移过来的用户快速上手。
参考仓库
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐


所有评论(0)