昇腾AI集群多维混合并行实战:从策略选型到性能调优
1. 从单卡到集群为什么多维混合并行是绕不开的坎大模型参数从百亿级往万亿级攀升的这几年单张NPU的显存和算力早就兜不住了。一张昇腾NPU的HBM容量再大也塞不下一个完整的大模型权重更别提训练时还有优化器状态、梯度、激活值这些额外开销。所以只要你想认真跑大模型集群就是必经之路。但集群不是把一堆卡插上电、连上网线就能跑起来的真正难的地方在于怎么把一个大模型合理地切分到几十上百张卡上让它们协同工作同时还要保证通信开销不把计算收益吃掉。这就是多维混合并行要解决的问题。简单说它是把数据并行、张量并行、流水线并行这几种切分策略组合起来用从多个维度同时拆分模型和数据的方案。你可以把它理解成一个三维甚至四维的切分空间数据维度切batch张量维度切矩阵乘法流水线维度切网络层有时候还有序列维度切长文本。每一维解决不同的问题组合起来才能把集群的算力真正榨干。我接触昇腾AI集群架构有段时间了从最早的纯数据并行踩到通信瓶颈到后来逐步引入张量并行和流水线并行中间踩过的坑真不少。这篇文章就把我对昇腾集群多维混合并行的理解、实操配置、调优经验完整梳理一遍。不管你是刚接触昇腾的新手还是已经在集群上跑过任务的老手应该都能从中找到一些有用的东西。文章会涉及HCCL通信库的配置、并行策略的选择逻辑、显存和通信的权衡计算以及一些实际跑任务时遇到的典型问题和排查方法。2. 昇腾AI集群的硬件底座与通信骨架2.1 NPU的硬件架构特点昇腾NPU和GPU在架构上有不少差异这些差异直接影响并行策略的设计。昇腾的AI Core采用达芬奇架构包含Cube矩阵计算单元、Vector向量计算单元和Scalar标量计算单元。Cube单元专门负责矩阵乘加运算这是大模型训练里最核心的计算模式。每个AI Core还有自己的L1 Buffer和L0 Buffer数据在各级缓存之间的搬运效率直接决定了算力利用率。从集群角度看昇腾NPU的互联能力是关键。以昇腾910系列为例单卡通过HCCS提供高速片间互联跨节点则依赖RoCE网络。这里有个很重要的概念叫超节点在超节点内部NPU之间通过HCCS全互联带宽远高于跨节点的RoCE。这意味着如果你的并行策略能把通信密集的操作尽量约束在超节点内部整体效率会高出一大截。实操心得选并行策略之前先搞清楚你的集群拓扑。哪些卡在同一个超节点内哪些卡跨节点这个信息决定了张量并行能开多大。张量并行对通信带宽极其敏感一般建议约束在超节点内部。2.2 HCCL通信库的角色HCCL是昇腾集群的集合通信库功能上对标NVIDIA的NCCL。它提供了AllReduce、AllGather、ReduceScatter、AlltoAll、Broadcast等集合通信原语这些原语就是多维混合并行的通信基础。不同的并行策略依赖不同的通信原语数据并行靠AllReduce同步梯度张量并行靠AllReduce和AllGather做结果聚合流水线并行靠Send/Recv传递中间激活专家并行则依赖AlltoAll做token路由。HCCL的性能调优有几个关键点。首先是通信域communication group的划分你需要根据并行策略创建不同的通信域把通信模式不同的操作隔离开。比如张量并行的通信域只包含同一流水线阶段内的卡数据并行的通信域则跨越所有流水线阶段。其次是通信算法的选择HCCL支持Ring、Tree等不同算法小数据量用Tree可能更快大数据量Ring更有优势。最后是通信与计算的overlap昇腾提供了异步通信的能力让通信在后台进行的同时计算继续跑。# HCCL通信域创建示意基于昇腾PyTorch适配层 import torch import torch_npu import torch.distributed as dist # 初始化分布式环境 dist.init_process_group(backendhccl) # 获取全局rank和world_size rank dist.get_rank() world_size dist.get_world_size() # 按并行策略划分通信域 # 假设 8卡tp2, pp2, dp2 tp_size, pp_size, dp_size 2, 2, 2 # 计算各维度的rank tp_rank rank % tp_size pp_rank (rank // tp_size) % pp_size dp_rank rank // (tp_size * pp_size) # 创建张量并行通信域同一pp阶段内tp组 tp_group dist.new_group(ranks[ pp_rank * tp_size i for i in range(tp_size) ]) # 创建数据并行通信域相同tp和pp位置的卡 dp_group dist.new_group(ranks[ dp_rank * tp_size * pp_size pp_rank * tp_size tp_rank for dp_rank in range(dp_size) ])上面这段代码展示了通信域划分的基本逻辑。实际项目中通信域的创建要结合具体的并行配置来设计核心原则是让通信模式一致的卡分到同一个组里。2.3 集群组网对并行策略的约束昇腾集群的组网方式直接约束了并行策略的选择空间。典型的组网是两层结构节点内通过HCCS互联节点间通过RoCE或参数面网络互联。节点内带宽通常是节点间的数倍甚至十倍以上。这个带宽差异意味着通信量大的并行维度应该尽量放在节点内。具体来说张量并行每层都要做AllReduce通信频率最高通信量也大所以张量并行组最好约束在节点内。流水线并行的通信只发生在阶段边界通信量相对小可以跨节点。数据并行的梯度同步虽然通信量大但可以通过梯度累积和通信overlap来掩盖跨节点也能接受。这里给一个经验性的带宽需求估算。假设模型参数量为P使用混合精度训练FP16张量并行度为tp那么每次前向传播中张量并行需要通信的数据量大约是2P/tp字节一次AllReduce。如果模型有L层每层都做一次总通信量就是2PL/tp。以千亿参数模型、80层为例tp8时单次前向的张量并行通信量约为20GB。这个量级如果跨节点走RoCE延迟会非常明显。3. 多维混合并行的策略拆解与选型逻辑3.1 数据并行最基础但并非万能数据并行是最直观的并行方式每张卡持有完整的模型副本各自处理不同的数据batch然后通过AllReduce同步梯度。它的优势是实现简单、扩展性好理论上加卡就能提升吞吐。但问题也很明显每张卡都要存一份完整的模型参数、梯度和优化器状态显存开销巨大。以Adam优化器为例FP16训练时每张卡需要存储模型参数2字节/参数、梯度2字节/参数、优化器一阶矩4字节/参数、优化器二阶矩4字节/参数、FP32主权重副本4字节/参数合计约16字节/参数。一个百亿参数模型光这些状态就要160GB显存单卡根本放不下。所以纯数据并行只适合小模型大模型必须结合模型并行。数据并行还有一个隐藏问题当并行度增大时梯度AllReduce的通信量线性增长而每张卡的计算量不变。这意味着存在一个临界点超过之后加卡反而变慢。昇腾上可以通过梯度分桶、通信计算overlap等手段缓解但无法根本消除。3.2 张量并行切矩阵乘法的艺术张量并行的核心思想是把矩阵乘法拆开让多张卡协作完成一个矩阵运算。以Transformer的注意力层为例Q、K、V的投影矩阵可以按列切分每个卡计算一部分注意力头最后拼接结果。MLP层的第一个线性层按列切第二个线性层按行切这样中间不需要额外的通信。张量并行的通信模式是前向传播时列切分的层需要AllGather聚合结果行切分的层需要AllReduce求和反向传播时通信模式反过来。每层都要通信所以对带宽要求极高。昇腾上一般建议张量并行度不超过8且约束在节点内。# 张量并行中列切分线性层的示意 class ColumnParallelLinear(torch.nn.Module): def __init__(self, in_features, out_features, tp_size): super().__init__() self.tp_size tp_size # 每个rank只持有 1/tp_size 的权重 self.weight torch.nn.Parameter( torch.empty(out_features // tp_size, in_features) ) def forward(self, x): # 本地矩阵乘法 local_out torch.matmul(x, self.weight.t()) # AllGather聚合所有rank的结果 gathered all_gather_along_last_dim(local_out, self.tp_group) return gathered张量并行的难点在于通信和计算的overlap。因为每层都要通信如果通信不能和计算重叠算力利用率会大幅下降。昇腾的HCCL支持异步通信可以在计算当前层的同时预取下一层需要的数据。实际调优时需要仔细安排通信和计算的顺序让两者尽可能并行。3.3 流水线并行按层切分的空间换时间流水线并行把模型按层切成多个阶段每个阶段放在不同的卡上。数据像流水线一样依次流过各个阶段。它的优势是通信量小只在阶段边界传递激活值可以跨节点扩展。但缺点是存在流水线气泡即某些阶段在等待输入时处于空闲状态。解决气泡的常用方法是微批次micro-batch。把一个大批次切成多个微批次让它们像流水线一样重叠执行。微批次越多气泡占比越小但显存开销也越大因为需要同时保存多个微批次的激活值。昇腾上常用的调度策略有GPipe和1F1BOne Forward One Backward后者通过交错执行前向和反向来减少气泡。流水线并行的阶段划分也有讲究。如果各阶段的计算量不均衡流水线效率会被最慢的阶段拖累。所以划分时要尽量让每个阶段的计算量相近。Transformer模型里每层的计算量基本一致所以均匀按层数切分通常就够了。但如果模型包含Embedding层或特殊的输出层这些层的计算量和普通层不同需要单独考虑。3.4 三种并行的组合逻辑单独用任何一种并行都有明显短板数据并行显存放不下张量并行通信太频繁流水线并行气泡难消除。组合起来用才能取长补短。典型的组合方式是流水线并行做粗粒度切分把模型分成几个阶段跨节点部署张量并行做细粒度切分在每个阶段内部把单层拆到多卡数据并行做副本扩展用多组副本提升吞吐。以一个千亿参数模型、64卡集群为例一种可行的配置是pp8tp4dp2。这样模型被切成8个流水线阶段每个阶段内部用4卡做张量并行然后整个系统有2个数据并行副本。总卡数8×4×264。这个配置下张量并行组4卡可以放在节点内流水线并行的跨阶段通信走节点间网络数据并行的梯度同步也走节点间。选型时需要考虑几个约束模型参数量决定最低的模型并行度tp×pp显存容量决定单卡能放多少参数集群拓扑决定tp能开多大吞吐需求决定dp开多少。这几个因素互相制约需要反复权衡。并行维度切分对象通信模式通信频率推荐部署范围数据并行BatchAllReduce每步一次跨节点张量并行矩阵运算AllReduce/AllGather每层一次节点内流水线并行网络层Send/Recv阶段边界跨节点序列并行序列长度AllGather每层一次节点内4. 实操配置从零搭起一个多维混合并行任务4.1 环境准备与依赖检查在昇腾集群上跑多维混合并行任务环境准备是第一步。需要确认的东西包括CANN版本、PyTorch适配版本、HCCL版本、驱动固件版本。这些组件之间有严格的版本对应关系版本不匹配会导致各种奇怪的错误。# 检查CANN版本 cat /usr/local/Ascend/ascend-toolkit/latest/version.cfg # 检查NPU设备状态 npu-smi info # 检查HCCL环境变量 env | grep HCCL # 确认PyTorch和torch_npu版本 python -c import torch; import torch_npu; print(torch.__version__, torch_npu.__version__)环境变量配置是容易被忽视但非常关键的一步。HCCL的行为受多个环境变量控制比如HCCL_IF_IP指定通信网卡HCCL_SOCKET_IFNAME指定socket网卡HCCL_BUFFSIZE控制通信缓冲区大小。这些变量配错了轻则性能下降重则通信失败。注意事项HCCL_BUFFSIZE不是越大越好。它占用的是NPU的HBM设太大反而会挤压模型显存。一般建议从默认值开始根据实际通信量逐步调整。我遇到过设成默认值两倍后显存不够导致OOM的情况。4.2 并行策略的配置与启动昇腾上实现多维混合并行通常基于Megatron-LM的适配版本或者MindSpeed框架。配置的核心是定义好各维度的并行度然后正确初始化进程组。# 启动脚本示例8节点每节点8卡共64卡 # pp8, tp4, dp2 export HCCL_IF_IP192.168.1.1 export HCCL_SOCKET_IFNAMEeth0 export HCCL_BUFFSIZE200 torchrun \ --nproc_per_node8 \ --nnodes8 \ --node_rank$NODE_RANK \ --master_addr$MASTER_ADDR \ --master_port6000 \ train.py \ --tensor-model-parallel-size 4 \ --pipeline-model-parallel-size 8 \ --data-parallel-size 2 \ --micro-batch-size 4 \ --global-batch-size 64 \ --seq-length 4096 \ --num-layers 80 \ --hidden-size 8192 \ --num-attention-heads 64这里有几个参数需要仔细算。global-batch-size等于micro-batch-size × dp_size × gradient_accumulation_steps。假设micro-batch-size4dp2想要global-batch-size64那gradient_accumulation_steps就是8。梯度累积的作用是在显存受限时模拟更大的batch但会增加训练步数。num-layers必须能被pipeline-model-parallel-size整除否则流水线阶段划分不均匀。80层分8个阶段每阶段10层刚好整除。如果模型层数不能被pp整除就需要做不均匀划分实现复杂度会上升。4.3 显存占用的估算与验证配置完并行策略后必须验证显存是否够用。显存占用主要包括模型参数、梯度、优化器状态、激活值、通信缓冲区。前四项可以用公式估算通信缓冲区需要实测。模型参数显存 参数量 × 精度字节数 / (tp × pp)。以千亿参数、FP16、tp4、pp8为例每卡参数显存 100B × 2 / (4 × 8) 6.25GB。梯度同样6.25GB。优化器状态Adam是参数的4倍FP32一阶矩二阶矩主权重即25GB。激活值取决于batch size和序列长度用activation checkpointing可以大幅降低。通信缓冲区按HCCL_BUFFSIZE算一般几百MB到几GB。加起来每卡大约需要40-50GB显存。昇腾910B的HBM是64GB勉强够用。如果不够可以增大tp或pp或者开启activation checkpointing、优化器状态分片ZeRO等技术。# 显存估算辅助函数 def estimate_memory(param_billion, tp, pp, dp, seq_len, hidden_size, micro_batch, precision_bytes2): params param_billion * 1e9 # 模型参数 param_mem params * precision_bytes / (tp * pp) # 梯度 grad_mem param_mem # 优化器状态AdamFP32 optim_mem params * 4 * 3 / (tp * pp) # 激活值粗略估算开启checkpointing后约1/3 activation_mem (micro_batch * seq_len * hidden_size * 40 * precision_bytes) / (tp * pp * 3) total (param_mem grad_mem optim_mem activation_mem) / 1e9 print(f每卡显存估算: {total:.2f} GB) return total estimate_memory(100, tp4, pp8, dp2, seq_len4096, hidden_size8192, micro_batch4)4.4 通信域的初始化顺序通信域的初始化顺序在多维混合并行里很关键。如果顺序不对可能出现死锁。一般的原则是先创建小范围的通信域如tp组再创建大范围的如dp组最后创建全局通信域。因为小范围通信域的创建只涉及少数卡同步开销小不容易出问题。另外昇腾的HCCL在创建通信域时会做一次握手如果某些卡还没准备好会阻塞等待。所以启动脚本里要确保所有进程几乎同时启动避免个别节点延迟导致整体卡住。实际部署时可以用torchrun的弹性启动功能或者用作业调度系统统一拉起所有进程。5. 性能调优把集群算力真正榨出来5.1 通信与计算的overlap多维混合并行的性能瓶颈往往不在计算而在通信。昇腾NPU的算力很强但如果通信不能和计算重叠算力利用率可能只有30%甚至更低。overlap的核心思路是在等待通信结果的同时让NPU去算不依赖该结果的部分。以流水线并行为例1F1B调度就是典型的overlap当某个微批次在做反向传播时下一个微批次的前向传播可以同时进行。这样计算和通信阶段间的激活传递就能重叠。张量并行里AllGather的结果需要用于后续计算但AllGather本身可以和前一个操作的计算重叠。# 通信计算overlap示意 def forward_with_overlap(x, weight, tp_group): # 异步发起AllGather handle dist.all_gather_async(local_tensor, grouptp_group) # 在等待通信的同时计算不依赖通信结果的部分 partial_result compute_independent_part(x) # 等待通信完成 handle.wait() # 合并结果 final_result combine(partial_result, gathered_tensor) return final_result实际调优时可以用昇腾的Profiling工具如msprof抓取时间线看通信和计算是否真的重叠了。如果发现通信时间没有被掩盖就要调整代码结构把通信尽量提前发起。5.2 梯度分桶与通信压缩数据并行的梯度AllReduce是通信大头。梯度分桶gradient bucketing是把多个小梯度拼成一个大buffer再通信减少通信次数。昇腾的HCCL对小消息的通信效率不高分桶后效果明显。桶的大小需要调太小起不到合并效果太大则增加显存开销和延迟。通信压缩是另一个思路。FP16梯度通信比FP32省一半带宽但可能影响收敛。更激进的还有梯度量化如8bit量化和稀疏化只传大梯度。这些方法在昇腾上都有实现但需要仔细验证对模型精度的影响。我的经验是FP16通信基本无损8bit量化在部分模型上会有轻微掉点稀疏化则要谨慎使用。5.3 流水线气泡的压缩流水线气泡是流水线并行的固有开销。气泡占比的公式是(pp-1)/(micro_batchespp-1)。假设pp8micro_batches16气泡占比就是7/23≈30%。这个开销相当大。增加micro_batches可以降低气泡占比但受限于显存。除了增加micro_batches还可以用交错式1F1B调度。它把每个阶段再细分成多个虚拟阶段让不同虚拟阶段的前向和反向交错执行进一步压缩气泡。昇腾的MindSpeed框架支持这种调度但配置复杂度更高。实操心得流水线气泡在训练初期影响最大因为那时候各阶段的计算时间还不稳定。建议先用小规模跑几百步等性能稳定后再看气泡占比。另外如果各阶段计算量不均衡气泡会更严重划分阶段时一定要做负载均衡。5.4 实测性能数据与调优案例分享一个我实际调过的案例。模型是70B参数集群是32卡昇腾910B节点内8卡HCCS互联节点间RoCE。初始配置是tp8pp4dp1。跑下来发现吞吐只有理论值的35%Profiling显示张量并行的AllReduce占了大量时间。分析后发现tp8跨了两个节点每节点8卡但tp组跨节点了导致张量并行的通信走了RoCE带宽只有HCCS的几分之一。调整方案是把tp降到4pp升到8这样tp组约束在节点内pp跨节点。调整后吞吐提升到理论值的62%。进一步优化开启梯度分桶桶大小设为256MB开启通信计算overlap调整micro-batch-size从2到4。最终吞吐达到理论值的78%。剩下的22%主要是流水线气泡和不可避免的通信开销。配置tpppdp吞吐理论值占比初始84135%调整tp/pp48162%加梯度分桶48170%加overlap48175%调micro-batch48178%这个案例说明并行策略的配置对性能影响巨大而且调优是个逐步迭代的过程。每一步优化都要用数据验证不能凭感觉。6. 常见问题与排查技巧实录6.1 通信超时与死锁通信超时是多维混合并行里最常见的问题。表现是任务卡住不动日志里出现HCCL timeout。原因通常有几类通信域创建顺序不一致导致死锁、某些卡的计算时间差异太大导致等待超时、网络配置问题导致部分卡通信不通。排查时先看日志确认是哪一步卡住的。如果是通信域创建阶段卡住检查各进程的创建顺序是否一致。如果是训练过程中卡住用npu-smi查看各卡利用率如果有的卡利用率100%有的0%说明是负载不均衡导致的等待。如果是网络问题用HCCL的测试工具做连通性测试。# HCCL连通性测试 # 在所有节点上执行 export HCCL_IF_IP本节点IP mpirun -np 16 -hostfile hostfile \ ./hccl_test -b 8K -e 1G -f 2 -d fp166.2 显存OOM的定位与解决OOM在混合并行里很常见但定位起来比单卡复杂因为涉及多个并行维度。首先要确认是哪张卡OOM然后分析该卡的显存构成。用torch_npu.npu.memory_allocated()可以查看当前显存占用。常见的OOM原因和解决方案激活值太大开启activation checkpointing或者减小micro-batch-size通信缓冲区太大调小HCCL_BUFFSIZE优化器状态太大使用ZeRO分片把优化器状态分散到多卡临时buffer峰值调整计算顺序避免同时分配大buffer注意事项昇腾NPU的显存碎片问题比GPU更明显。长时间训练后即使总空闲显存够也可能因为碎片导致OOM。解决办法是定期做显存整理或者预留一部分显存不用。6.3 精度异常与loss震荡混合并行下精度问题比单卡更难排查因为涉及多个卡的数值聚合。常见问题包括loss突然变成NaN、loss震荡不收敛、不同卡上的loss不一致。loss变NaN通常是梯度爆炸或数值溢出。可以先检查是否有卡的计算结果异常用torch.isnan()逐层排查。如果是个别卡的问题可能是该卡的输入数据有问题。如果是普遍问题可能是学习率太大或梯度裁剪没生效。不同卡loss不一致通常是数据并行同步出了问题。检查AllReduce是否正常执行梯度是否真的同步了。有时候是通信域配置错误导致某些卡没参与同步。6.4 性能不达预期的排查路径性能不达预期时按以下路径排查先用Profiling工具抓时间线看时间花在哪里如果通信占比高检查通信域划分是否合理tp是否跨了节点如果计算占比高但算力利用率低检查是否有算子没走Cube单元如果气泡占比高增加micro-batches或调整流水线调度如果数据加载是瓶颈检查数据预处理是否在NPU上做是否用了多线程加载问题现象可能原因排查方法解决方案通信超时通信域顺序不一致检查日志和创建顺序统一创建顺序OOM激活值/通信buffer过大查看显存分布开checkpointing/调bufferloss NaN梯度爆炸逐层检查数值加梯度裁剪/降学习率吞吐低tp跨节点Profiling看通信时间调整tp/pp配置气泡大micro-batch少计算气泡占比增加micro-batch6.5 集群扩展时的注意事项从单节点扩展到多节点时有几个容易踩的坑。首先是网络配置多节点需要正确配置RoCE网卡和路由否则通信会走默认网关导致性能极差。其次是时钟同步多节点训练对时钟同步有要求时间偏差太大会导致通信超时。最后是故障恢复大规模集群里单卡故障是常态需要配置checkpoint定期保存和自动恢复机制。我在扩展集群时遇到过一次典型问题从8卡扩到32卡后吞吐不升反降。排查发现是新加入的节点网络配置有问题RoCE网卡没绑定正确导致跨节点通信走了低速通道。修正网络配置后吞吐恢复正常。这个教训是扩展集群前一定要先做网络连通性和带宽测试。7. 一些个人体会多维混合并行这个东西理论看着清晰实操起来细节极多。我最大的体会是没有万能配置只有针对特定模型和特定集群的最优配置。同样的并行策略换个模型、换个集群拓扑效果可能完全不同。所以调优一定要基于实测数据不能照搬别人的配置。另一个体会是通信优化的重要性不亚于计算优化。很多人把精力都花在算子优化上但实际瓶颈往往在通信。把通信域划分好、把通信和计算overlap做好性能提升可能比优化几个算子大得多。最后昇腾生态还在快速演进CANN和HCCL的版本更新会带来性能变化。建议定期关注版本更新日志有时候一个新版本就能解决困扰很久的性能问题。我在升级CANN版本后HCCL的AllReduce性能提升了近20%这种收益是白捡的。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →