
前言分布式训练做多了会发现多卡之间的通信往往比计算更吃时间。八张昇腾NPU跑一个LLaMA-70BAllReduce在总耗时里能占30-40%这个比例在卡数更多的时候还会继续涨。昇腾CANN的hcclHuawei 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-AllReducehccl的AllReduce有两种实现路径自动根据拓扑选择。Ring-AllReduce8卡以内最优。数据在环上转一圈每个卡只跟左右邻居通信延迟低。流程Step1: Scatter-Reduce数据累加 NPU0 → NPU1 → NPU2 → ... → NPU7 → NPU0 每步累加 1/8 的数据7步完成 Step2: AllGather结果广播 NPU7 → NPU6 → ... → NPU0 → NPU7 每步广播 1/8 的结果7步完成总通信量2×(N-1)×D/NN卡数D数据大小。Tree-AllReduce16卡以上最优。数据像树一样往下传带宽利用率高。流程Root / \ NPU0 NPU1 / \ / \ NPU2 NPU3 NPU4 NPU5数据从叶子节点往根节点累加Reduce再从根节点往叶子节点广播Broadcast总通信量更少。实测数据Ascend 910HCCS互联消息大小128MB卡数Ring-AllReduce 延迟(ms)Tree-AllReduce 延迟(ms)最优选择21218Ring42832Ring86558Tree1614289Tree32310156Tree工程经验8卡是个拐点hccl里有个HCCL_ALGO_THRESHOLD环境变量改这个可以强制选Ring或者Tree。调试的时候先试默认如果带宽利用率不到70%手动设HCCL_ALGO_THRESHOLD16强制用Tree。通信-计算重叠的实现传统做法里反向传播的梯度算完然后调用AllReduce做同步计算和通信串行NPU的Cube单元在通信的时候是闲置的。hccl支持通信-计算重叠梯度算完一部分就可以开始AllReduce同时Cube继续算下一部分的梯度两个操作在时间上重叠。实现原理hccl把梯度tensor切成若干chunk每个chunk单独做AllReduce。Cube算完chunk i立刻启动chunk i的AllReduce同时Cube继续算chunk i1。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-7B8卡Ascend 910FP16chunk大小重叠率端到端吞吐(TPS)提升1MB23%3200基准4MB58%410028%16MB72%480050%64MB68%460044%256MB51%390022%chunk16MB最优重叠率72%。工程经验chunk大小不是固定的跟模型大小有关。7B模型chunk16MB最优70B模型chunk64MB最优梯度更大通信时间更长需要更大的chunk来隐藏延迟。hccl里有自动chunk选择逻辑但默认配置偏保守chunk4MB建议手动设HCCL_CHUNK_SIZE1677721616MB。HCCS 互联与 RoCE 跨节点通信Ascend 910的卡间互联有两种HCCSHuawei Cache Coherent System和RoCERDMA over Converged Ethernet。HCCS同一台服务器内的卡间通信带宽56GB/s延迟1μs。8卡以内走HCCS。RoCE跨服务器通信带宽取决于网卡通常是100Gb/s~200Gb/s Ethernet延迟~10μs。16卡以上开始走RoCE。hccl自动选择通信路径同服务器内走HCCS跨服务器走RoCE。不需要上层代码配置。实测带宽利用率消息大小128MB通信路径理论带宽实际带宽利用率HCCS8卡内56GB/s48GB/s86%RoCE 100Gb/s12.5GB/s9.8GB/s78%RoCE 200Gb/s25GB/s21GB/s84%工程经验RoCE的带宽利用率在消息大小16MB的时候掉得很厉害只有40-50%因为RDMA的启动开销占比大。hccl里有个消息合并逻辑多个小消息合并成一个大消息再发这个逻辑默认是关的要手动开HCCL_ENABLE_MSG_MERGE1。开了之后小消息的带宽利用率能到70%。与 NCCL 的接口对齐hccl的接口设计尽量跟NCCLNVIDIA的通信库对齐从GPU平台迁移过来的代码改动量很小。PyTorch DDP的backend切换# GPU 版本dist.init_process_group(backendnccl,...)# 昇腾 NPU 版本dist.init_process_group(backendhccl,...)就改一个参数模型代码不需要动。如果有自定义的通信逻辑比如MoE模型里的Expert Parallel可以直接调hccl的C接口// hccl C接口示例hcclComm_tcomm;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上的性能数据FP168卡HCCS互联原语消息大小带宽(GB/s)利用率AllReduce1MB1221%AllReduce16MB3868%AllReduce128MB4886%AllGather16MB4275%AllGather128MB5191%ReduceScatter16MB4071%ReduceScatter128MB4988%AlltoAll16MB3563%AlltoAll128MB4479%消息大小16MB的时候带宽利用率才上得来因为通信启动开销是~15μs消息太小启动开销占比大。跟NCCL在A100上的性能比hccl的AllReduce在消息大小64MB的时候差距在10%以内消息大小16MB的时候差距在25%左右hccl的启动开销更大。踩坑实录坑18卡AllReduce带宽利用率只有40%原因默认用的是Ring-AllReduce8卡的时候Tree更快但hccl默认选了Ring。解决设HCCL_ALGO_THRESHOLD8强制8卡也用Tree。设完带宽利用率从40%涨到72%。坑2跨节点通信RoCE带宽利用率只有30%原因消息大小太小4MBRDMA启动开销占比大。解决开消息合并HCCL_ENABLE_MSG_MERGE1把多个小消息合并成一个大消息再发。开了之后带宽利用率涨到68%。坑3通信-计算重叠率只有20%远低于预期的70%原因chunk大小设得太小1MB通信启动开销占比大。解决设HCCL_CHUNK_SIZE1677721616MB重叠率从20%涨到72%端到端吞吐涨50%。坑432卡训练AllReduce在总耗时里占50%原因32卡走RoCE但RoCE的带宽只有HCCS的1/4AllReduce时间太长。解决改用Pipeline Parallel把32卡拆成4个pipeline stage每stage 8卡stage内做Tensor Parallelstage间做Pipeline Parallel。AllReduce的范围从32卡缩小到8卡通信时间降75%。https://atomgit.com/cann/hcclhttps://atomgit.com/cann/hcommhttps://atomgit.com/cann/ascend-boost-comm