国产 GPU/NPU 适配实战:从 CUDA 迁移到异构加速
摘要: 国产算力(昇腾、寒武纪、沐曦、天数等)已进入真实生产集群,但"把 CUDA 代码迁过去"不是换个编译器那么简单:生态、算子、显存管理、性能特征全不同。本文给出一条可落地的迁移路线:盘点 CUDA 依赖 → 选迁移路径 → 算子映射 → 性能验收 → 双栈共存。
0. 实验目标
- 盘点应用对 CUDA 的真实依赖(决定迁移路径)
- 掌握三条迁移路线:框架层迁移 / 算子重写 / 双栈共存
- 了解昇腾(Ascend)与寒武纪(Cambricon)的适配要点
- 建立性能验收标准:正确性 + 精度 + 性能回退容忍度
1. 先盘点:你的应用到底依赖 CUDA 什么
bash
# 盘点工具链依赖:源码里出现的关键 CUDA 符号
grep -rE "cudaMalloc|cudaMemcpy|cublas|nccl|torch.cuda|numba.cuda" --include="*.py" --include="*.cu" --include="*.cpp" . | head -20
# 检查运行环境动态库依赖
ldd /path/to/your_binary | grep -iE "cuda|cudnn|cublas|nccl"逐段拆解:
- 依赖分四类:框架 API(
torch.cuda,最易迁)、CUDA runtime API(cudaMalloc等,中等)、库调用(cuBLAS/cuDNN/NCCL,由新栈提供)、手写 kernel(.cu/.pyx,最难)。 - 结论先行:80% 的应用卡在"框架 API + 库调用"层——走框架层迁移即可,只有深度学习内核研究者才需要动
.cu。
2. 三条迁移路线
| 路线 | 做法 | 成本 | 适用 |
|---|---|---|---|
| A. 框架层迁移 | PyTorch 模型 + torch_npu/torch_mlu 等适配层 | 低(改 device 与少量 API) | 绝大多数训练/推理应用 |
| B. 算子重写 | Triton/CANN/MLU 自定义算子替代手写 CUDA | 中高 | 有自研 kernel 的热点模型 |
| C. 双栈共存 | CUDA 与国产卡分池共存,按负载路由 | 中 | 迁移过渡期、容灾 |
原理讲解:
- 路线 A 的核心:适配层把
torch.cuda映射到国产卡运行时(昇腾叫 CANN,寒武纪叫 CNToolkit),并补齐torch.compile/AMP/NCCL 对应物(HCCL/CNCL 通信库)。多数模型只需改device="cuda"→ 适配层暴露的设备名。 - 路线 C(过渡期标配):CUDA 池跑存量业务,国产池跑新业务/备份,Slurm/K8s 用分区或标签路由(详见本知识库 Slurm 异构调度 与 K8s GPU 调度 篇)。
3. 昇腾适配要点(示例)
bash
# 安装 CANN + torch_npu
pip install torch_npu
export ASCEND_HOME=/usr/local/Ascend/ascend-toolkit
# 代码改动示例:torch.cuda → 昇腾设备
import torch
import torch_npu # 注册 npu 设备
device = "npu:0" # 原 "cuda:0"
model = model.to(device)
x = x.to(device)工程启示(昇腾):
torch_npu提供npu:0设备,但并非所有算子都有 NPU 实现——遇到不支持的算子会报错或回退 CPU,性能会掉。先跑torch_npu的算子支持清单核对。- 混合精度:昇腾支持 FP16/BF16,AMP 路径与 CUDA 类似;通信库是 HCCL(对应 NCCL),DDP 代码几乎不动。
- 推理部署:MindSpore/MindIE 是昇腾生态的另一条路径(框架层二选一)。
4. 寒武纪适配要点(示例)
bash
# CNToolkit + torch_mlu
pip install torch_mlu
export LD_LIBRARY_PATH=$CNTOOLKIT_HOME/lib64:$LD_LIBRARY_PATH
# 代码改动示例
import torch_mlu # 注册 mlu 设备
device = "mlu:0" # 寒武纪 MLU 设备工程启示(寒武纪):
- MLU 的算子覆盖与性能特征和 NVIDIA 有差异:小算子场景 MLU 相对吃亏,大 GEMM/卷积类算子接近——迁移后先跑算力画像(算子级 profile),别拿整体 benchmark 猜。
- 通信库是 CNCL;
torch.compile支持程度需按版本核对(Triton 后端适配成熟度不一)。
5. 性能验收:迁移成功的标准
bash
# 验收三步:正确性 → 精度 → 性能
# 1) 正确性:同一输入,NVIDIA 与国产卡输出对齐
python compare_outputs.py --ref cuda --test npu --tolerance 1e-3
# 2) 精度:训练收敛曲线对比(loss 曲线形状一致)
# 3) 性能:同 batch 同模型,对比 step 耗时与吞吐
python bench.py --device npu --batch 32验收标准建议:
| 项 | 标准 |
|---|---|
| 输出正确性 | 与 CUDA 结果 diff < 1e-3(FP32)/ 同量级(AMP) |
| 训练收敛 | loss 曲线趋势一致,最终精度差 < 1% |
| 性能 | 达 CUDA 基线 60-90%(按场景;算子画像决定) |
| 稳定性 | 长跑 72h 无 OOM/挂死 |
关键认知:迁移的验收不是"跑通"而是"可对比"。性能回退要量化到算子级——知道掉在哪、为什么掉(算子覆盖/显存带宽/通信库),才有优化抓手。
6. 工程启示汇总
- 先盘点再动手:80% 应用走框架层迁移(路线 A),别一上来写算子。
- 适配层是核心:torch_npu/torch_mlu 把
torch.cuda映射到国产运行时,多数代码只改 device。 - 算子覆盖是第一个坑:跑官方支持清单,不支持的算子回退 CPU 会掉性能。
- 通信库对位:NCCL→HCCL(昇腾)/CNCL(寒武纪),DDP 代码基本不动。
- 算力画像再下结论:整体 benchmark 猜不出问题,算子级 profile 才知道掉在哪。
- 双栈共存过渡:CUDA 与国产卡分池,按负载路由,逐步切换。
- 验收量化:正确性/精度/性能/稳定性四项,性能回退量化到算子级。
7. 延伸阅读
- 昇腾 CANN 文档
- 寒武纪 CNToolkit 文档
- 本知识库:07. 异构计算 章节 · Slurm 异构调度 · K8s GPU 调度 · CUDA MPS