迁移到昇腾 910C 的第一周,我遇到了三个报错:一个算子回退到 CPU、一个 out of memory、一个 HCCL 超时。
2025 年 5 月,昇腾 910C 开始大规模供货(据工控网报道)。这颗通过先进封装集成两颗 910B 逻辑裸片的芯片,被市场视为对标 NVIDIA H100 的关键产品。据华为昇腾专家调研纪要,2024 年昇腾 910 系列全年总出货量约 35 万片,互联网厂商约 12 万片。当国产算力进入规模化部署阶段,开发者面对的命题已经从“能不能买”变成“怎么跑顺”。
我们花了三周把一套 PyTorch 图像分类管线(ResNet-50 + ViT)从 A100 迁移到 8 卡昇腾 910C 集群。基础环境:CANN 8.0.RC1,torch_npu 1.12.1,每卡 24GB 显存。目标是在 224×224 的 ImageNet 上做混合精度训练。
算子兼容:从 CUDA 到 NPU 不止是改 device
把 torch.device('cuda') 换成 torch.device('npu') 只是开始。最隐蔽的坑是数据类型隐式转换。昇腾训练卡对 float64 支持较差,很多算子会直接回退到 CPU。我们的 dataloader 原本用了 torch.set_default_dtype(torch.float64)——在 A100 上毫无问题,迁移后直接爆出“算子不支持”。修法:全局显式使用 float32 或混合精度半精度。
另一个高频踩点是 torch.bmm。PyTorch 的 torch.bmm 在昇腾上可能回退到 CPU(与算子实现版本有关),性能从几毫秒掉到几十毫秒。改用 torch.matmul + view 替代,保持 NPU 执行:
# 原写法
out = torch.bmm(x, y)
# 替代写法
out = torch.matmul(x, y) # NPU 上 matmul 优化更好
建议迁移前先跑一遍昇腾算子支持列表,把所有 NotSupport 的算子做替换计划。更简单的办法是开启 torch.npu.is_available() 后,用 torch.jit.trace 定位第一个回退算子。
内存墙:显存占满但算力跑不满
迁移初期,每卡 batch size 从 128 降到 64 依然 OOM。观察 npu-smi 发现显存占用 95%,但算力利用率只有 30% 左右。这跟我们在《显存占满,GPU却没跑满:2026年算力账单该从哪里查起》里分析的情况高度一致——显存碎片 + 数据供给不足。
昇腾 910C 显存管理默认采用“按需分配”,频繁创建释放 tensor 会造成空洞。我们在每次 epoch 前手动清空缓存:
torch.npu.empty_cache()
同时把 dataloader 的 pin_memory=True 改为 pin_memory_device='npu',减少 host-device 搬移开销。
混合精度和梯度检查点压内存的利器。ViT 加入梯度检查点后,显存占用直接降 40%,batch size 回到 128。
多卡通信:HCCL 超时魔咒
8 卡 DDP 训练,第一次启动就卡在“NCCL 超时”类错误(昇腾上用 HCCL 替代 NCCL)。检查日志发现是 hccl 初始化多次调用冲突。
确保 init_process_group 只调用一次,backend='hccl':
if not dist.is_initialized():
dist.init_process_group(backend='hccl', init_method='env://', rank=rank, world_size=world_size)
昇腾 910C 节点内通信带宽不错,跨节点建议配置 RoCE 网卡。华为官方文档推荐 export HCCL_OVER_ROCE=1 并配置 IB 或 RoCE 网卡 IP。
性能调优:从跑通到跑好
稳定性通过后,目标是与 A100 对比(相同 FP16,batch 128)。初始性能只有 A100 的 60%。
图模式加速
PyTorch 1.12+ 的 torch.compile 在昇腾上对应 torch_npu.compile。实测 ViT 编译后提升 25%:
model = torch_npu.compile(model, backend='inductor') # 需要 CANN 8.0 以上
注意编译时间较长(约 15 分钟),但运行时 graph 重建后前后端都提速。
数据预取
昇腾的 DataLoader 默认 num_workers=0 会让 CPU 成为瓶颈。我们调至 num_workers=8,并启用 prefetch_factor=4,让 GPU 利用率从 60% 提升到 78%。
算子替代
把模型中不适合 NPU 的算子替换:F.instance_norm 性能差,用 F.batch_norm 替代(在不影响精度的层);torch.stack 拆成 torch.cat + view,减少临时张量。
最终性能达到 A100 的 82%,混合精度下记数相差不多。
那些让你怀疑人生的报错
| 错误信息 | 原因 | 解决 |
|---|---|---|
[ERROR] ASCEND[Operator] Not supported op: xxx |
算子不在当前 CANN 版本支持列表 | 查询昇腾算子列表,替换为等价实现 |
RuntimeError: NPU out of memory |
显存不足或碎片 | 检查 batch size,加入 torch.npu.empty_cache(),开启梯度检查点 |
HCCL timeout at step 0 |
多卡初始化竞争 | dist.init_process_group 只调用一次,检查环境变量 RANK/WORLD_SIZE |
迁移不是翻译
昇腾 910C 迁移的真实感受:它不是简单地把 cuda 替换成 npu,而是重新理解硬件特性。显存、带宽、算子实现各有脾气。建议先单卡跑通小模型,逐步加数据。
对于准备迁移的团队,先做两部分工作:
- 用
torch_npu.dump导出算子级性能快照,找到 Top 3 慢算子; - 参考华为昇腾社区(Hugging Face 的 Ascend 模型库)已有模型实现,减少重复踩坑。
我们的迁移历时 21 天,从 OOM 到稳定训练,再到吞吐接近 A100 的 85%。国产算力生态在快速成熟,但当下的价值在于“可调试”,而不是“开箱即用”。如果你正在迁移其他模型,可以对比《换模型那两周:一家汽车零部件厂的 ViT-Lite 质检升级账本》中的思路——国产化之路,每一步细节都值得记下来。