前言单张昇腾NPU的算力已经够用了——910B的单卡算力是256 TFLOPSFP16,足以跑多数模型的单卡推理。但训练就不一样了。训练大模型时单卡显存放不下整个模型,单卡算力也不够在一个合理的时间内跑完训练。分布式训练成了必须的选择,而分布式训练的核心是集合通信。HCCLHuawei Collective Communication Library就是CANN生态中负责多卡通信的组件。搞过分布式训练的人都经历过通信成为瓶颈的痛苦。8卡训练时,每步迭代的计算时间可能只要200毫秒,但AllReduce的通信时间要150毫秒——通信占比超过40%,等于有将近一半的时间NPU不是在算而是在等数据。通信成为瓶颈的原因有两个层面。一是硬件层面——NPU的计算吞吐增长快于显存带宽和互联带宽的增长。以昇腾910B为例,FP16的计算能力是256 TFLOPS,显存带宽是1.5 TB/s,互联带宽是56 GB/s。计算能力跟互联带宽的比例大约是4500:1——意味着你所有的NPU每做一次浮点计算,对应的中间结果需要通过互联带宽传输0.22字节。在大模型训练中,每个浮点计算的梯度大约需要传输0.5到1个字节。这说明互联带宽的供给跟不上计算量对通信的需求,通信一定会成为瓶颈。二是软件层面——通信操作本身的开销。一次AllReduce操作不只是一个数据传输,还包括数据切分、通信环构建、Reduce操作的计算、同步屏障等。这些步骤加起来的时间通常比纯数据传输时间多20%到30%。8卡训练时,每步迭代的计算时间可能只要200毫秒,但AllReduce的通信时间要150毫秒——通信占比超过40%,等于有将近一半的时间NPU不是在算而是在等数据。HCCL要解决的正是这个问题在昇腾NPU的多卡拓扑下,把集合通信做到尽可能快。它内部集合了多种通信算法——Ring AllReduce、Tree AllReduce、Hierarchical AllReduce——并根据卡间拓扑自动选择最优算法。这篇文章从HCCL的架构出发,讲清楚HCCL怎么做到高效通信,以及在实践中怎么排查和优化通信瓶颈。HCCL在CANN中的位置HCCL是一套集合通信库,但它不是独立的。它工作在CANN Runtime之上,通过Runtime提供的NPU间通信原语来实现数据交换。你在PyTorch中用torch.distributed做多卡训练时,底层调用的其实是Torch-NPU插件通过AscendCL调用HCCL的接口。HCCL不直接控制NPU的硬件——它定义通信的模式和数据流的路径,Runtime负责实际执行。# HCCL在分布式训练调用链中的位置 import torch import torch.distributed as dist import torch_npu # 初始化进程组 dist.init_process_group(backendhccl, rankrank, world_size8) # 这行代码的底层链路 # 1. torch.distributed - Torch-NPU backend - AscendCL # 2. AscendCL - HCCL 初始化通信域 # 3. HCCL - Runtime 建立NPU间通信通道 model MyModel().npu() # 数据并行训练中的梯度同步 # 每个step反向传播结束后 optimizer.step() # 触发参数的AllReduce同步 # 底层链路 # 1. torch_npu梯度同步 - HCCL AllReduce # 2. HCCL选择通信算法Ring/Tree/Hierarchical # 3. HCCL - Runtime 执行NPU间的数据交换 # 4. 所有NPU拿到梯度均值后更新参数WHYHCCL选择Ring AllReduce作为默认通信算法在昇腾910B的8卡HCCS直连拓扑中,每张卡有6个HCCS华为缓存一致性接口端口连接到其他卡。Ring AllReduce利用这种拓扑构建一个逻辑环——每张卡只与前后相邻的两张卡通信。当参与通信的NPU数量N增大时,Ring AllReduce的通信数据量是2*(N-1)/N倍总数据量——当N很大时趋近于2倍总数据量,不随N增加。相比之下,Tree AllReduce的通信量跟N的对数成正比,当N较小时8卡Ring更快,N较大时64卡以上Tree的优势开始显现。HCCL根据world size自动选择主算法2到8卡默认Ring AllReduce,16卡以上引入Hierarchical AllReduce。HCCL的通信拓扑探测HCCL的最优通信策略取决于NPU之间的物理拓扑。昇腾服务器的NPU拓扑通常有三种单机多卡通过HCCS直连、跨机通过100Gb RoCE或InfiniBand互联、多机多卡同时有HCCS和RDMA混合连接。HCCL在初始化通信域时会自动探测拓扑,随后为每组NPU对选择最短的通信路径。# HCCL的拓扑探测与通信路径选择 import torch import torch_npu from torch_npu.contrib import transfer_to_npu # 多机多卡训练的基础配置 WORLD_SIZE 32 # 4台机器 × 8卡 RANK rank # 当前进程的全局编号 LOCAL_RANK local_rank # 当前进程在本地机器上的编号 # HCCL初始化时自动做拓扑感知 dist.init_process_group( backendhccl, world_sizeWORLD_SIZE, rankRANK ) # HCCL内部拓扑探测流程纯示意,非真实API # 1. HCCL读取/etc/hccn.conf中的拓扑描述文件 # 2. 或者通过NPU驱动获取HCCS连接矩阵 # 3. 构建卡间连接拓扑图 # 4. 为每组NPU对计算最短路径 # 5. 初始化通信域,分配通信缓冲区在昇腾910B的8卡服务器中,NPU之间的连接不是全连接的。典型的服务器内部拓扑是双环结构——NPU0-3形成一个环,NPU4-7形成另一个环,环之间通过两个桥接NPUNPU3和NPU4连接。如果两个NPU在同一环内,它们之间的通信延迟很低小于1微秒。如果两个NPU在不同环,通信需要经过桥接NPU转发,延迟增加。HCCL的拓扑感知确保通信数据走最短路径——环内通信直接走HCCS直连,环间通信走桥接NPU转发。没有拓扑感知的粗暴实现会让环内通信也走桥接路径,增加不必要的延迟。通信域的概念HCCL中的通信域Communicator是一个逻辑概念——它定义了一组NPU之间的通信关系。默认的全局通信域WORLD包含所有参与训练NPU。但实际训练中可能只需要子集通信——比如数据并行训练中,梯度同步只需要模型并行组内部的NPU参与,不需要整个集群的NPU都参与。HCCL支持创建多个通信域来满足不同的并行策略需求。典型的分布式训练配置会创建多个通信域数据并行域所有NPU、模型并行域每张卡内部、流水线并行域跨机分组。每个通信域独立选择通信算法。import torch_npu import torch.distributed as dist # 在模型并行场景中创建子通信域 # 假设8卡做2路模型并行 4路数据并行 def setup_parallel_groups(world_size8, mp_size2): # 模型并行组每连续的2张卡构成一个MP组 mp_groups [] for i in range(0, world_size, mp_size): mp_group dist.new_group(rankslist(range(i, imp_size))) mp_groups.append(mp_group) # 数据并行组每张卡跨MP组取相同index的卡 dp_groups [] for i in range(mp_size): dp_group dist.new_group(rankslist(range(i, world_size, mp_size))) dp_groups.append(dp_group) return mp_groups, dp_groups mp_groups, dp_groups setup_parallel_groups() # 模型并行组内的通信使用AllReduce # 数据并行组内的通信也使用AllReduce # 但两个组的通信域独立,互不干扰在混合并行训练中,模型参数的梯度同步只发生在数据并行组内——不同模型并行副本之间的参数不需要同步。如果所有参数都走全局通信域的AllReduce,每个NPU都会收到其他7张卡的梯度数据,其中大部分是跟自己无关的。通信数据量是必要的4倍。通过创建子通信域,每个NPU只跟同组内的NPU交换数据,通信量大幅减少。拓扑描述文件的配置HCCL的拓扑信息通过rank表文件来描述。这个JSON格式的文件定义了每个NPU的编号、IP地址、端口号、以及所在服务器的节点ID。在多机训练中,rank表文件必须在所有节点上保持一致——如果有一台机器的rank表文件和其他的不一致,通信域初始化会失败。{ version: 1.0, server_count: 4, server_list: [ { server_id: 10.0.0.1, device: [ {device_id: 0, device_ip: 10.0.0.1:6000, rank_id: 0}, {device_id: 1, device_ip: 10.0.0.1:6001, rank_id: 1}, {device_id: 2, device_ip: 10.0.0.1:6002, rank_id: 2}, {device_id: 3, device_ip: 10.0.0.1:6003, rank_id: 3}, {device_id: 4, device_ip: 10.0.0.1:6004, rank_id: 4}, {device_id: 5, device_ip: 10.0.0.1:6005, rank_id: 5}, {device_id: 6, device_ip: 10.0.0.1:6006, rank_id: 6}, {device_id: 7, device_ip: 10.0.0.1:6007, rank_id: 7} ] } ], status: completed }分布式训练中每张NPU需要知道其他NPU在哪里。rank表文件做的就是这个地址解析工作——每个rank_id对应一个device_ip和端口号,HCCL用这个信息来建立NPU之间的RDMA或者HCCS连接。如果rank表文件配置错误——比如IP写错了或者rank_id分配重复——通信域初始化时会连接超时。在排错时,开头先检查rank表文件的内容是否正确,这是最常见的通信初始化失败原因。NCCL到HCCL的迁移如果你之前使用NCCL做分布式训练,迁移到HCCL时需要注意几个差异。第一是backend名称不同——NCCL的backend是’nccl’,HCCL的backend是’hccl’。第二是环境变量的前缀不同——NCCL_开头的变量在HCCL中对应HCCL_开头的版本。第三是HCCL的通信域初始化需要rank表文件的支持,而NCCL通常只需要MASTER_ADDR和MASTER_PORT两个环境变量。从NCCL迁移到HCCL的最简单方法是使用Torch-NPU插件——它代理了PyTorch的distributed模块,把backendhccl’的调用映射到HCCL的接口。对于标准的数据并行训练,只需要改一个参数backendnccl’改成backend‘hccl’。对于混合并行训练,需要检查通信域的创建逻辑——Torch-NPU对子通信域的创建做了适配,保证创建的通信域使用HCCL的接口。# NCCL到HCCL的迁移示例 import torch import torch.distributed as dist # NCCL版本 # dist.init_process_group(backendnccl, ...) # HCCL版本——只需要改backend dist.init_process_group(backendhccl, ...) # 如果使用Torch-NPU,还需要初始化NPU设备 torch_npu.npu.set_device(local_rank)Torch-NPU插件实现了PyTorch的ProcessGroup抽象接口。当backendhccl’时,Torch-NPU会实例化一个HCCL ProcessGroup——它对外暴露的接口跟NCCL ProcessGroup完全一致all_reduce、all_gather、broadcast等,只是底层实现的通信库不同。这种抽象层的设计使得从GPU切换到NPU时,分布式训练代码的改动量最小。如果你的代码中使用了NCCL特有的环境变量比如NCCL_P2P_DISABLE,NCCL_IB_DISABLE,需要替换为对应的HCCL版本——HCCL_P2P_DISABLE,HCCL_RDMA_DISABLE。HCCL的通信算法选择HCCL内部实现了多种集合通信算法,并在运行时根据通信域大小、数据量、拓扑等因素自动选择最优算法。每种算法针对不同的场景做了优化。Ring AllReduce适用于小数据量的频繁同步——每个NPU只跟邻居通信,带宽利用率在单环拓扑中很高。Tree AllReduce适用于大数据量的同步——带宽利用率更高但延迟也更高。Hierarchical AllReduce结合了两者——机内用Ring做快速同步,机间用Tree做跨机同步。# HCCL的算法选择逻辑纯概念示意 class HCCLAlgorithmSelector: def select_algorithm(self, comm_size, data_bytes, topology): if comm_size 8: # 小规模集群优先Ring AllReduce # Ring的通信延迟与通信域大小无关 if data_bytes 1 * 1024 * 1024: # 1MB return RingAllReduce(algorithmring) else: return RingAllReduce(algorithmring) else: # 大规模集群使用Hierarchical # 机内用Ring,机间用Tree return HierarchicalAllReduce( intra_nodering, inter_nodetree, # 机内组大小通常为8 intra_node_size8 )Ring AllReduce虽然延迟不随NPU数量增长,但它的带宽利用率受限于单环拓扑。当卡间连接不是完整的双向环时——比如只有单HCCS连接——Ring的带宽利用率只有50%。而Tree AllReduce更灵活,可以在任意拓扑中工作,但它需要log(N)轮通信,当N较小时轮数少但每一轮的数据量更大。HCCL用混合策略来平衡——小集群用Ring对延迟友好,大规模集群用Hierarchical对带宽友好。通信计算的OverlapHCCL的一个重要优化是通信和计算的重叠overlap。在反向传播过程中,梯度的计算和同步不需要严格串行——你可以一边计算某层的梯度一边把已计算好的梯度发送出去。HCCL支持通过分桶bucket策略来实现这种重叠。PyTorch DDP的默认行为是把所有梯度收集到一个大桶中,反向传播完成后一次性做AllReduce。但这意味着整步迭代的计算和通信是串行的。HCCL的分桶策略将梯度按计算顺序分成多个小桶,每个桶计算完成后立即触发AllReduce——计算和通信在时间上重叠。# PyTorch DDP的梯度同步分桶配置 from torch.nn.parallel import DistributedDataParallel as DDP model DDP( model, device_ids[local_rank], # 分桶大小越小重叠越多但通信碎片化 bucket_cap_mb25, # broadcast_buffers在每次前向传播前同步buffer broadcast_buffersTrue ) # 分桶策略的效果 # 默认bucket_cap_mb25意味着每25MB的梯度做一次AllReduce # 减小这个值如10MB可以增加计算和通信重叠 # 增大这个值如50MB可以减少通信碎片但重叠减少 # 最优值取决于模型大小和通信带宽WHY分桶需要调优分桶大小直接影响计算通信重叠的效率。桶太小,AllReduce的次数增加,每次AllReduce的固定开销启动延迟、同步开销占了通信时间的大头——假设每次AllReduce的启动延迟是50微秒,100次AllReduce就是5毫秒的额外延迟。桶太大,AllReduce的等待时间变长——计算层往后推进但通信还没完成,最终还是要等。最佳分桶大小取决于模型的计算图和NPU的计算速度——计算密集的层用大桶,通信带宽充裕时用小桶。实际调优中从bucket_cap_mb25开始尝试,观察训练吞吐量变化,调整到吞吐量不再显著提升时停止。通信瓶颈的排查HCCL提供了多种工具来分析通信性能。最常用的是HCCL Profiler——它可以采集每次集合通信的执行时间、数据量、延迟等信息,帮助定位通信瓶颈。# HCCL通信性能分析 import os # 启用HCCL的Profiling os.environ[HCCL_PROFILING_ENABLE] 1 # 训练完成后查看Profiling输出 # HCCL会生成通信日志,包含 # - 每次集合操作的起始时间 # - 每次集合操作的数据量 # - 每次集合操作的执行时长 # - 通信域的拓扑信息 # - 通信环的构建信息 # 常见的通信瓶颈模式 # 1. 梯度同步时间计算时间 - 通信带宽不足 # 2. 卡间时间差异大 - 负载不均衡或者某条链路有问题 # 3. 步间时间波动大 - 网络拥塞或者主机侧干扰通信瓶颈的诊断不能靠猜测。Profiling数据能告诉你每次AllReduce花了多长时间——如果8卡AllReduce 1GB数据花了20毫秒,而理论带宽计算的期望时间只有10毫秒,说明通信没有达到硬件上限。还有一个常见的诊断场景是卡间时间差异过大。在理想情况下,多卡训练中所有NPU完成一次AllReduce的时间应该非常接近。如果某张卡的AllReduce时间比其他卡长很多——比如其他卡都是15毫秒,某张卡是30毫秒——说明这张卡所在链路上存在问题。常见的原因包括HCCS链路未完全连接本应该是6个端口只连了4个、RDMA网卡速率协商不一致一个100Gb一个50Gb、以及PCIe链路降速。通过HCCL Profiling的链路级数据,可以准确定位是哪条链路的问题。除了AllReduce的Profiling,HCCL还提供通信链路级别的诊断工具。hccl_tools是HCCL的配套诊断工具,可以测试每对NPU之间的点对点带宽和延迟。在部署大规模的分布式训练集群之前,先用hccl_tools跑一遍全链路带宽测试——确认每张NPU到其他NPU的带宽都达到了硬件规格。这是排查硬件问题的标准步骤。# HCCL链路带宽测试伪代码概念 # 实际使用hccl_tools命令行工具 $ hccl_tools -t bandwidth -n 8 # 输出格式 # NPU0 - NPU1: 52.3 GB/s (预期56 GB/s) # NPU0 - NPU2: 51.8 GB/s (预期56 GB/s) # NPU0 - NPU3: 23.1 GB/s (预期56 GB/s) ← 疑似链路问题 # ...分布式训练跑起来之后发现通信瓶颈,排查起来很麻烦。是软件配置的问题还是硬件链路的问题如果先跑了hccl_tools链路测试,所有硬件链路的带宽数据都有了基线——哪些卡之间走HCCS直连,哪些走桥接,哪些走RDMA,各自的带宽和延迟是多少。训练中实际通信时间和带宽基线的差距就是软件优化需要解决的问题——如果实际带宽只有基线的一半,说明HCCL的ring构建没有充分利用硬件拓扑。如果实际带宽跟基线一致但训练整体还是慢,说明通信不是瓶颈,问题是出在计算端。实际排查中链路测试比Profiling更高效——链路测试在几秒内完成全链路扫描,Profiling需要跑完整的训练step。建议在第一次部署分布式训练时先做链路测试,保存测试结果作为后续排查的参考基线。当训练性能下降时,重新跑一次链路测试,对比基线的差异。HCCL通信优化的实践建议在实际的分布式训练优化中,有几个实用的HCCL调优方向。第一个是调整bucket大小——如果计算密集而通信延迟高,减小bucket值让梯度尽早开始同步,增加计算和通信的重叠时间。如果通信带宽充裕但碎片化严重,增大bucket值让通信更集中。第二个是检查通信域的划分——在混合并行训练中,确保每个通信域的规模跟并行策略匹配。数据并行通信域的大小应该是模型并行组的大小——比如你做的是8路数据并行加4路模型并行,数据并行通信域的大小应该是32而不是2。通信域划分错误是分布式训练中最容易犯的错误——划分太大会引入不必要的通信,划分太小会导致梯度同步不完全。第三个是使用固定通信缓冲区——HCCL的默认行为是在每次通信时分配临时缓冲区,通信结束后释放。这种动态分配在高频通信场景中会导致显存碎片化。设置HCCL_BUFFER_SIZE为较大的值如128MB可以让HCCL预先分配固定大小的缓冲区,避免通信过程中的显存分配和释放。第四个是适当增加通信超时时间——在集群负载较重的训练环境中,通信偶尔会因网络拥塞而暂时延迟。HCCL的默认超时时间120秒对大多数场景足够,但在慢盘或者CPU负载高的环境中可能出现超时。适量增加到300秒可以减少偶发超时导致的训练中断。超时时间的设置需要在稳定性和故障检测及时性之间做平衡——时间太短训练不稳定,太长会让真正的问题显卡上。使用前后的效率对比——如果8卡AllReduce 1GB数据花了20毫秒,而理论带宽计算的期望时间只有10毫秒,说明通信没有达到硬件上限。可能的原因是通信环构建不合理——卡间HCCS链路未完全利用。另一个常见问题是通信buffer分配不连续——HCCL需要在通信前把不连续的数据拷贝到连续buffer中,这个拷贝开销可能占到通信时间的20%到30%。HCCL的通信原语详解除了AllReduce,HCCL还实现了其他集合通信原语AllGather、ReduceScatter、Broadcast、Reduce。每种原语在分布式训练中都有特定的用途。AllGather用于张量并行——每个NPU从其他NPU收集切片数据,拼接成完整张量。ReduceScatter用于数据并行的梯度同步——每个NPU把自己维护的梯度跟其他NPU对应位置的梯度做求和规约,结果分布在各个NPU上。Broadcast用于参数初始化——将rank 0的初始参数广播到其他NPU。这些原语在HCCL中的实现基于统一的通信框架——都是先构建通信环或通信树,随后在环/树上执行对应的数据流模式。Ring AllReduce实际上是ReduceScatter加AllGather的组合——第一轮在环上做ReduceScatter把梯度分布到各卡,第二轮做AllGather把规约后的结果广播到各卡。HCCL对这种组合做了融合优化——如果检测到连续的ReduceScatter和AllGather操作,它们被合并为一个AllReduce kernel避免中间状态的同步开销。# 不同并行策略对应的HCCL原语选择 import torch.distributed as dist # 场景1数据并行——梯度同步用AllReduce # 每张卡维护完整的模型副本 # 反向传播后所有卡的梯度求均值 def sync_gradients_data_parallel(model): for param in model.parameters(): if param.grad is not None: dist.all_reduce(param.grad, opdist.ReduceOp.SUM) param.grad / dist.get_world_size() # 场景2模型并行——前向传播用AllGather # 模型参数分布在多张卡上 # 前向传播时需要收集完整的中间结果 def forward_model_parallel(local_output, group): # local_output是当前卡维护的部分输出 # 收集所有卡的输出拼接为完整结果 full_output [torch.zeros_like(local_output) for _ in range(group.size())] dist.all_gather(full_output, local_output, groupgroup) return torch.cat(full_output, dim-1) # 场景3流水线并行——相邻stage用P2P通信 # 每个NPU负责模型的一段连续层 # 前向传播时按顺序传递中间激活 def forward_pipeline_parallel(hidden, prev_npu, next_npu): # 从前一个stage接收输入 if prev_npu is not None: dist.recv(hidden, srcprev_npu) # 当前stage的计算 hidden stage_forward(hidden) # 发送到下一个stage if next_npu is not None: dist.send(hidden, dstnext_npu) return hiddenWHY不同原语在不同并行策略中的效率差异大AllReduce在数据并行中是最优选择——它一次操作就完成了梯度求和与广播。但在模型并行中,每个NPU需要收集的是完整的中间结果而不是局部求和的梯度,用AllGather更合适——AllReduce的额外规约操作在模型并行中用不到。在流水线并行中,通信只发生在相邻stage之间,用P2P的send/recv就够了,用AllReduce反而是浪费。选对通信原语可以让训练中的通信数据量减少一半以上。HCCS直连的硬件特征HCCL利用昇腾910B的HCCS直连能力做机内通信。每张910B NPU通过6个HCCS端口对外连接——每个端口的带宽是56GB/s单向。在标准的Atlas 800T A2训练服务器中,8张NPU的HCCS端口按照特定拓扑连接。HCCL通过HCCL_ALGO环境变量可以强制使用特定的通信链路。HCCS直连的一个特征是它在主机内存和NPU显存之间的通信路径。传统的PCIe直连需要数据经过CPU内存做中转——NPU显存的数据先拷贝到CPU内存,再通过PCIe传输到目标NPU。HCCS直连可以实现NPU之间的显存直接读写——数据不经过CPU内存,延迟更低。在多机训练中,这种直连能力的优势更为明显——传统的PCIe通信方案中,跨机通信需要经过GPU-系统内存-网卡-交换机-目标机器系统内存-GPU的路径,每一步都引入延迟和带宽瓶颈。HCCS直连配合RDMA网卡可以实现NPU显存到远程NPU显存的直接读写。HCCL的多流并发HCCL支持多流并发通信。在昇腾NPU上,硬件有多个DMA引擎可以同时处理不同方向的通信任务。HCCL利用这个特性,在通信数据量大时将数据拆分为多个独立的stream,每个stream处理一部分数据的传输。多流并发在跨机训练场景中效果尤为明显。当使用多个RDMA网卡做跨机通信时,HCCL可以将数据分发到不同的RDMA网卡上同时传输。比如在4台机器每台8卡共32卡的配置中,跨机AllReduce的数据量可能是单机Ring AllReduce的几倍。如果不做多流并发,数据需要排队经过单一RDMA网卡传输,带宽利用不充分。HCCL的多流并发实现可以让多个RDMA网卡并行工作,把跨机通信延迟降低30%到50%。# HCCL的多流配置通过环境变量 import os # 启用多流通信 os.environ[HCCL_STREAM_NUM] 4 # 使用4个流 # 或者由HCCL自动检测 os.environ[HCCL_AUTO_STREAM] 1增加stream数量可以提升带宽利用率,但也会增加额外的开销。每个stream需要独立的通信缓冲区——stream从4个增加到8个意味着通信缓冲区也从4份增加到8份,占用更多显存。另外,多stream需要处理stream之间的同步——如果一个stream传输完成而其他stream还没完成,需要等待最慢的那个stream。在带宽不是瓶颈的场景中比如小数据量的频繁同步,单stream的延迟更低。HCCL的自动stream检测功能根据通信数据量自动决定stream数量——数据量大于stream_num×buffer_size时增加stream,反之减少。在16卡以上的大规模训练场景中,多台服务器之间通过RDMA网络连接。HCCL的Hierarchical AllReduce策略在这里发挥作用——机内8卡通过HCCS做Ring AllReduce,随后将机内的规约结果通过RDMA发送到指定的全局规约节点。跨机通信的次数从所有的NPU对之间压缩为不同机器代表节点之间的单次通信。HCCL的核心优化在分布式训练的不同场景中表现如下场景使用前通用通信策略使用后HCCL优化策略差异来源8卡数据并行AllReduce使用默认的NCCL-ish实现,单环通信,带宽利用率约50%拓扑感知的双环并行,带宽利用率提升到85%以上利用HCCS拓扑的双环结构并行做Ring AllReduce32卡多机训练机内机间统一使用单一算法,大规模集群扩展性差Hierarchical AllReduce,机内Ring机间Tree,通信量按级别分层分层策略减少了跨机通信的频次和数据量计算通信Overlap全部梯度计算完成后统一同步,通信时间完全是串行开销分桶同步,每桶梯度计算完成后立即通信,通信时间被计算掩盖反向传播的梯度计算与通信并行化混合并行通信域全部NPU参与同一个通信域,通信量是实际需要的数倍子通信域隔离,只有同组NPU互相通信,数据量精确到并行策略根据并行策略创建对应的通信域,避免跨组通信从表中可以看出,通信优化的关键不是优化单次通信的速度——单次通信的速度受制于硬件带宽,软件能做的事情有限。通信优化的关键是减少不必要的通信——合适的算法选择减少通信量,合适的拓扑感知减少转发跳数,合适的通信域划分减少参与通信的NPU数量。HCCL把这几个维度的优化组合起来,让实际训练中的通信占比从40%以上降低到15%到25%。仓库地址https://atomgit.com/cann/hccl