CANN 的"残余 10%"几乎全部集中在 MoE 路由与通算融合、混合精度敏感层、以及分布式通信原语这三块,而不是普通 torch.* 算子。下面这张表我就按这个真实边界来拉,分三张子表,每张都给到 PyTorch 算子/原语粒度、CANN 现状、坑位表现、替代方案与代价。
CANN vs CUDA:残余 10% 坑位清单(PyTorch 算子粒度)
The 90% is marketing. The 10% is where models live or die.
90% 是市场话术,10% 才决定模型是活还是死。
边界声明:CANN 8.x + torch-npu 2.5/2.6/2.7 对 torch.* 命名空间下基础算子(add / matmul / softmax / layer_norm / conv2d …)覆盖率 ≥95%,这部分不是坑。真正的 10% 是"自定义 CUDA 内核 + 控制流 + 跨算子融合 + 分布式通信原语",以下逐条拆解。
表一 · MoE 路由与通算融合(最深的坑,约占总坑位 50%)
这是 DeepSeek-V4 / Qwen-MoE / Mixtral 类模型真正的战场。CANN 已有 npu_moe_init_routing_v2、npu_moe_re_routing、MoeTokenPermuteWithEP、MoeTokenUnpermuteWithEP、npu_moe_gating_top_k(_softmax) 等融合算子,但边界和代价都很具体。
|
# |
PyTorch / 用户写法 |
CUDA 做法 |
CANN 现状 |
坑位表现 |
替代方案 & 代价 |
|---|---|---|---|---|---|
|
1 |
|
自定义 CUDA kernel 一步完成 |
|
分步写法在 64 experts / top-8 下 kernel launch 开销 > 计算本身 |
改调 NPU 融合算子,需改代码,且 sigmoid 路由要换 |
|
2 |
|
Triton kernel 原地重排 |
|
朴素实现内存读写 4 次,NPU 上带宽瓶颈先于算力 |
用 CANN 融合算子,输出布局与 CUDA 不一致,下游 expert 计算要适配 |
|
3 |
|
自定义反向 scatter |
|
反向路由索引若用 PyTorch 原生,回退 CPU 极慢 |
必须用 NPU 融合版,否则 MoE 层吞吐掉 40%+ |
|
4 |
|
NCCL all_to_all_single |
HCCL |
通信与计算串行时,all-to-all 把省下的算力全吃回去——这是 MoE 在 NPU 上"没比稠密快"的头号原因 |
必须上 MC2 通算融合(通信与 expert 计算流水线化),否则无效 |
|
5 |
|
Triton grouped_gemm |
|
稀疏度高(top-1/2、64+ experts)收益大;密集激活时重排开销抵消收益 |
稀疏度低时反向不融合反而更快,需按 top-k/expert 数动态选择 |
|
6 |
反向 |
NCCL + 自定义反向内核 |
HCCL 支持,但反向梯度路由逻辑需额外 kernel |
训练时反向通算融合复杂度远高于推理,当前 GMM 融合主要面向推理 |
训练态要么退化为串行通信,要么自写 Ascend C 反向 kernel |
|
7 |
动态路由(路由结果随训练步变化) |
Triton 动态 shape kernel |
控制流融合困难,硬件向量化执行分支代价高 |
动态 shape 下融合算子失效,回退到多个小算子 |
用 |
一句话定性:MoE 在 CANN 上"能跑"和"跑得跟 CUDA 一样快"之间,隔着一个 MC2 通算融合是否覆盖你这个 top-k / expert 数组合的问题。没覆盖到的组合,性能断崖。
表二 · 混合精度敏感算子(精度漂移重灾区,约占总坑位 30%)
CANN 官方文档明确点名了哪些算子"必须用 FP32 算、不能跟着半精度走"——这些就是 FP16/BF16 训练时 loss 漂移、收敛差的源头。
|
# |
PyTorch 算子 |
精度敏感原因 |
CANN 行为 |
坑位表现 |
处理方案 |
|---|---|---|---|---|---|
|
8 |
|
指数运算 + 分母累加,FP16 易上/下溢 |
官方要求 FP32 输入,底层只接受 FP32 |
误用 FP16 输入 → loss 爆炸或 silently 漂移 |
强制 upcast,或开动态 loss scale |
|
9 |
|
算方差涉及累加,低精度误差累积 |
LayerNorm/RMSNorm 必须用 FP32 算,PyTorch 无原生 RMSNorm 易写错 |
用 FP16 跑 LN → 收敛差 2–3%、训练不稳定 |
自实现时强制 |
|
10 |
|
Attention 分数 × V 的 matmul 需 FP32 保数值稳定 |
CANN 6.0 不原生支持 SDPA,会回退到 matmul+softmax 朴素实现(慢 3×);7.0+ 才路由到 FlashAttention |
老版本 CANN 上 Attention 既慢又易溢出 |
升级 CANN ≥7.0,或显式调用 |
|
11 |
|
大 LLM 注意力分数易上溢 |
需 FP32 累加 |
FP16 累加 → 注意力分布塌缩 |
用 |
|
12 |
梯度 |
梯度/激活值累加 |
必须 upcast 到 FP32 通信,否则数值不稳定 |
半精度通信 → 多卡训练精度较单卡低 1–3% |
通信前 cast,或用 |
|
13 |
动态 Loss Scale 检查 |
NPU 浮点异常语义与 GPU 不同 |
昇腾浮点异常检查机制与 GPU 有差异,频繁 scale 变化敏感 |
动态 loss scale 抖动 → 收敛失败 |
改静态 loss scale(128/256/512/1024),或 |
|
14 |
Embedding / 输出层 |
第一层、最后一层对精度极敏感 |
官方要求 FP32 |
半精度 embedding → 困惑度升 2 点 |
强制 FP32 输入层与 LM head |
|
15 |
|
动态图跟踪 |
CANN 7.0 前部分动态图跟踪失败 |
trace 静默出错 → 推理结果偏差 |
改用 |
一句话定性:混合精度的坑不是"不支持 FP16",而是哪些层绝对不能跟半精度——CANN 文档列得很清楚,踩中的人会以为是硬件问题,其实是精度策略没对齐。
表三 · 分布式通信原语与版本耦合(迁移摩擦,约占总坑位 20%)
|
# |
PyTorch 分布式原语 |
CUDA 栈 |
CANN / HCCL 现状 |
坑位表现 |
处理方案 |
|---|---|---|---|---|---|
|
16 |
|
NCCL |
需改为 HCCL 后端,接口语义近似但超时/建链语义不同 |
直接复制 GPU 代码 → 建链失败或 hang |
用 |
|
17 |
|
NCCL |
HCCL 支持,但临时 tensor 未释放会导致内存上涨(CANN 7.0 才修复) |
长训 OOM,肉眼难定位 |
升级 CANN ≥7.0,或手动 |
|
18 |
|
NCCL |
CANN 7.0 才原生支持不等长 allgather/reducescatter |
老版本处理变长序列 → 报错或 hack |
升级或 padding 到等长 |
|
19 |
|
CUDA 图编译 |
CANN 2.6.0 预览版仍不支持 inductor |
依赖 inductor 的模型直接报错 |
用 |
|
20 |
第三方 CUDA 扩展(apex / xformers / flash-attn) |
C++/CUDA 内核 |
未针对 NPU 编译则完全无法运行 |
import 报错,或静默走 CPU fallback 性能断崖 |
用 |
|
21 |
|
NCCL |
CANN 7.0 才支持通过 |
多通信组调优失效 |
升级或用环境变量替代 |
|
22 |
硬件故障感知 / 弹性训练 |
CUDA 无对应 |
CANN 7.0 新增硬件延时故障感知与恢复 |
老版本卡死无感知 |
升级 + 配 MindCluster 容错 |
一句话定性:通信原语本身 HCCL 已对齐 NCCL 95%+,真正的摩擦是版本矩阵——CANN / torch_npu / PyTorch 三者必须严格配套,错一版就 runtime 异常或静默降级。
收口:10% 到底卡在哪
把三张表压缩成一张决策图:
|
模型类型 |
主要踩坑区 |
能否"零代码迁移" |
必须改代码的环节 |
|---|---|---|---|
|
稠密 LLM(Qwen-72B 类) |
表二(精度)+ 表三(版本) |
基本可以(≥95% 算子 1:1) |
FlashAttention 调用、loss scale 策略、通信后端 |
|
MoE 大模型(DeepSeek-V4 / Qwen-MoE) |
表一(MoE 通算融合)为主 |
不可以 |
Gate 路由、Permute/Unpermute、AllToAll、反向梯度路由 |
|
视觉 / 检测(YOLO / DINO 类) |
表三(apex / torchvision C++ ops) |
部分可以 |
NMS、IoU 等需 |
|
动态图 / RL 训练(PPO / GRPO) |
表二(动态 shape)+ 表三 |
较难 |
|
The 90% gets you a demo. The 10% gets you a production cluster.
90% 让你跑通 demo,10% 决定你能不能上生产集群。
所以"CUDA 生态代差 5–10 年"这个判断,落到 PyTorch 算子粒度上,精确说法是:
基础算子 95% 已 1:1 映射,残余 10% 集中在 (a) MoE 通算融合的覆盖边界、(b) 混合精度敏感层的精度策略、(c) 分布式通信原语的版本耦合——这三块不是"缺算子",是"缺覆盖组合 + 缺文档对齐 + 缺版本纪律",修的是工程而非科学。
此文由 怡心湖 编辑,若您觉得有益,欢迎分享转发!:首页 > 常识论 » CANN vs CUDA:残余 10% 坑位清单(PyTorch 算子粒度)
硅、栈与主权:AI 时代中国 GPU 国产