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(backend='hccl') # 获取全局rank和world_size rank = dist.get_rank() world_size = dist.get_world_size() # 按并行策略划分通信域 # 假设 8卡:tp=2, pp=2, dp=2 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层为例,tp=8时,单次前向的张量并行通信量约为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和1F1B(One Forward One Backward),后者通过交错执行前向和反向来减少气泡。
流水线并行的阶段划分也有讲究。如果各阶段的计算量不均衡,流水线效率会被最慢的阶段拖累。所以划分时要尽量让每个阶段的计算量相近。Transformer模型里,每层的计算量基本一致,所以均匀按层数切分通常就够了。但如果模型包含Embedding层或特殊的输出层,这些层的计算量和普通层不同,需要单独考虑。
3.4 三种并行的组合逻辑
单独用任何一种并行都有明显短板:数据并行显存放不下,张量并行通信太频繁,流水线并行气泡难消除。组合起来用才能取长补短。典型的组合方式是:流水线并行做粗粒度切分,把模型分成几个阶段跨节点部署;张量并行做细粒度切分,在每个阶段内部把单层拆到多卡;数据并行做副本扩展,用多组副本提升吞吐。
以一个千亿参数模型、64卡集群为例,一种可行的配置是:pp=8,tp=4,dp=2。这样模型被切成8个流水线阶段,每个阶段内部用4卡做张量并行,然后整个系统有2个数据并行副本。总卡数=8×4×2=64。这个配置下,张量并行组(4卡)可以放在节点内,流水线并行的跨阶段通信走节点间网络,数据并行的梯度同步也走节点间。
选型时需要考虑几个约束:模型参数量决定最低的模型并行度(tp×pp),显存容量决定单卡能放多少参数,集群拓扑决定tp能开多大,吞吐需求决定dp开多少。这几个因素互相制约,需要反复权衡。
| 并行维度 | 切分对象 | 通信模式 | 通信频率 | 推荐部署范围 |
|---|---|---|---|---|
| 数据并行 | Batch | AllReduce | 每步一次 | 跨节点 |
| 张量并行 | 矩阵运算 | 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卡 # pp=8, tp=4, dp=2 export HCCL_IF_IP=192.168.1.1 export HCCL_SOCKET_IFNAME=eth0 export HCCL_BUFFSIZE=200 torchrun \ --nproc_per_node=8 \ --nnodes=8 \ --node_rank=$NODE_RANK \ --master_addr=$MASTER_ADDR \ --master_port=6000 \ 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-size=4,dp=2,想要global-batch-size=64,那gradient_accumulation_steps就是8。梯度累积的作用是在显存受限时模拟更大的batch,但会增加训练步数。
num-layers必须能被pipeline-model-parallel-size整除,否则流水线阶段划分不均匀。80层分8个阶段,每阶段10层,刚好整除。如果模型层数不能被pp整除,就需要做不均匀划分,实现复杂度会上升。
4.3 显存占用的估算与验证
配置完并行策略后,必须验证显存是否够用。显存占用主要包括:模型参数、梯度、优化器状态、激活值、通信缓冲区。前四项可以用公式估算,通信缓冲区需要实测。
模型参数显存 = 参数量 × 精度字节数 / (tp × pp)。以千亿参数、FP16、tp=4、pp=8为例,每卡参数显存 = 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_bytes=2): params = param_billion * 1e9 # 模型参数 param_mem = params * precision_bytes / (tp * pp) # 梯度 grad_mem = param_mem # 优化器状态(Adam,FP32) 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, tp=4, pp=8, dp=2, seq_len=4096, hidden_size=8192, micro_batch=4)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, group=tp_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_batches+pp-1)。假设pp=8,micro_batches=16,气泡占比就是7/23≈30%。这个开销相当大。增加micro_batches可以降低气泡占比,但受限于显存。
除了增加micro_batches,还可以用交错式1F1B调度。它把每个阶段再细分成多个虚拟阶段,让不同虚拟阶段的前向和反向交错执行,进一步压缩气泡。昇腾的MindSpeed框架支持这种调度,但配置复杂度更高。
实操心得:流水线气泡在训练初期影响最大,因为那时候各阶段的计算时间还不稳定。建议先用小规模跑几百步,等性能稳定后再看气泡占比。另外,如果各阶段计算量不均衡,气泡会更严重,划分阶段时一定要做负载均衡。
5.4 实测性能数据与调优案例
分享一个我实际调过的案例。模型是70B参数,集群是32卡昇腾910B,节点内8卡HCCS互联,节点间RoCE。初始配置是tp=8,pp=4,dp=1。跑下来发现吞吐只有理论值的35%,Profiling显示张量并行的AllReduce占了大量时间。
分析后发现,tp=8跨了两个节点(每节点8卡,但tp组跨节点了),导致张量并行的通信走了RoCE,带宽只有HCCS的几分之一。调整方案是把tp降到4,pp升到8,这样tp组约束在节点内,pp跨节点。调整后吞吐提升到理论值的62%。
进一步优化:开启梯度分桶,桶大小设为256MB;开启通信计算overlap;调整micro-batch-size从2到4。最终吞吐达到理论值的78%。剩下的22%主要是流水线气泡和不可避免的通信开销。
| 配置 | tp | pp | dp | 吞吐(理论值占比) |
|---|---|---|---|---|
| 初始 | 8 | 4 | 1 | 35% |
| 调整tp/pp | 4 | 8 | 1 | 62% |
| 加梯度分桶 | 4 | 8 | 1 | 70% |
| 加overlap | 4 | 8 | 1 | 75% |
| 调micro-batch | 4 | 8 | 1 | 78% |
这个案例说明,并行策略的配置对性能影响巨大,而且调优是个逐步迭代的过程。每一步优化都要用数据验证,不能凭感觉。
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/调buffer |
| loss NaN | 梯度爆炸 | 逐层检查数值 | 加梯度裁剪/降学习率 |
| 吞吐低 | tp跨节点 | Profiling看通信时间 | 调整tp/pp配置 |
| 气泡大 | micro-batch少 | 计算气泡占比 | 增加micro-batch |
6.5 集群扩展时的注意事项
从单节点扩展到多节点时,有几个容易踩的坑。首先是网络配置,多节点需要正确配置RoCE网卡和路由,否则通信会走默认网关导致性能极差。其次是时钟同步,多节点训练对时钟同步有要求,时间偏差太大会导致通信超时。最后是故障恢复,大规模集群里单卡故障是常态,需要配置checkpoint定期保存和自动恢复机制。
我在扩展集群时遇到过一次典型问题:从8卡扩到32卡后,吞吐不升反降。排查发现是新加入的节点网络配置有问题,RoCE网卡没绑定正确,导致跨节点通信走了低速通道。修正网络配置后,吞吐恢复正常。这个教训是:扩展集群前,一定要先做网络连通性和带宽测试。
7. 一些个人体会
多维混合并行这个东西,理论看着清晰,实操起来细节极多。我最大的体会是:没有万能配置,只有针对特定模型和特定集群的最优配置。同样的并行策略,换个模型、换个集群拓扑,效果可能完全不同。所以调优一定要基于实测数据,不能照搬别人的配置。
另一个体会是,通信优化的重要性不亚于计算优化。很多人把精力都花在算子优化上,但实际瓶颈往往在通信。把通信域划分好、把通信和计算overlap做好,性能提升可能比优化几个算子大得多。
最后,昇腾生态还在快速演进,CANN和HCCL的版本更新会带来性能变化。建议定期关注版本更新日志,有时候一个新版本就能解决困扰很久的性能问题。我在升级CANN版本后,HCCL的AllReduce性能提升了近20%,这种收益是白捡的。