☰
从CUDA到昇腾:DeepSeek适配背后的技术迁移之路
2026/10/8 3:51:15 网站建设 项目流程

1. 假期里的这条消息,为什么值得放下手里的瓜

1.1 先还原一下发生了什么

春节假期那几天,我本来在乡下烤火刷手机,结果技术群里突然炸了。DeepSeek 和华为两家,趁着大多数人都在休假,把一件对国内 AI 算力生态影响挺大的事往前推了一大步——DeepSeek 系列模型完成了对华为昇腾(Ascend)平台的深度适配,并且不是简单"能跑"的那种适配,而是从算子层到训练框架层做了系统性打通。结合华为这边 CANN 计算架构和 MindSpore 生态的持续迭代,"CUDA 国产替代"这个喊了好几年的口号,终于有了一个看得见摸得着的技术底座。

我当时的第一反应是:这事儿的象征意义和实际意义都很大,但网上很多讨论都停留在"华为牛""DeepSeek 牛"的情绪层面,很少有人把技术路径拆开讲清楚。作为一个这几年一直在折腾 GPU 计算、也被 CUDA 生态绑得死死的开发者,我觉得有必要把这里面的门道好好捋一捋——CUDA 到底凭什么这么难替代?所谓"替代"替代的究竟是什么?如果你现在手上有一批模型要迁移,会碰到哪些具体的坑?

1.2 为什么说这是"干成了一件大事"

说它大,不是说华为昇腾的芯片性能突然超过了 NVIDIA 的旗舰卡——至少在通用计算峰值上,还没有人能拍胸脯说全面超越。真正的大事在于,过去 CUDA 的护城河不只是硬件性能,而是整条软件栈:从 GPU 驱动、底层的 PTX 指令集,到 cuBLAS、cuDNN 这些计算库,再到 PyTorch、TensorFlow 这些框架里的 CUDA 后端,最后到 Millions 的开发者已经写好的 CUDA 代码。你要撬动这条链,任何一个环节断了都白搭。

DeepSeek 做的事情,等于在模型这一层给出了一个高价值的"示范案例":一个大体量、高关注度的开源模型,不依赖 CUDA 也能在国产芯片上顺利训练和推理。华为做的事情,等于在工具链这一层把"让模型跑起来"所需的全部底盘补齐了。两件事合在一起,才是完整的"替代"叙事。这比单纯发布一块新芯片要难得多,因为你在和一个积累了十几年的生态对抗。

2. CUDA 的护城河到底有多深

2.1 CUDA 不只是"GPU 的编程语言"

很多刚接触的同学会把 CUDA 理解成"一种给显卡写代码的语言",这个理解没错,但远远不够。CUDA 是一个完整的并行计算平台,它包含三层东西:第一层是硬件抽象,让你不用关心 GPU 内部到底有多少个 SM、寄存器怎么分配,你只需要写内核函数,让成千上万个线程并行执行;第二层是高性能计算库,比如做矩阵乘法的 cuBLAS、做深度学习加速的 cuDNN、做线性代数求解的 cuSOLVER,这些库都是 NVIDIA 的工程师用汇编级优化一点点抠出来的,同一个矩阵乘法,你用现成库和自己手写,性能差距可以到几十倍;第三层是开发工具链,包括编译器 nvcc、性能分析器 ncu/nvprof、调试器 cuda-gdb,以及跟 PyTorch、TensorFlow 深度绑定的运行时。

我经常用一个类比来解释这层关系:CUDA 就像一个极其成熟的"操作系统"——不是 Windows 那种图形界面系统,而是那种你看不见、但所有软件都跑在它上面的底层系统。你写 PyTorch 代码的时候,torch.cuda.FloatTensor背后走的是 CUDA 的显存管理和内核调度;你用torch.matmul,它自动调到 cuBLAS 里那些手工优化的矩阵核函数。开发者平时根本感受不到 CUDA 的存在,但一旦要换平台,才发现自己写的每一行代码底下都垫着一层 CUDA。

2.2 生态锁定的三个层次

我这些年做 GPU 计算优化,最大的体会是 CUDA 的锁定是分三个层次逐级加深的:

第一层是代码级锁定。只要你的项目里写过 CUDA kernel,或者用到了 CUDA 特有的 API,比如自管理显存、自定义 stream 并发,那迁移的时候这些代码基本得重写。比较讽刺的是,很多项目里的 CUDA 代码其实是当年从别人那儿抄来的,或者是从 Stack Overflow 上拼出来的,作者自己都不一定完全理解,现在要重写,难度直接拉满。

第二层是库级锁定。就算你一行 CUDA 代码都没写过,只要你的 PyTorch 模型用到了 cuDNN 里的卷积优化算法、或者通过 TensorRT 做过推理加速,那你的性能基准就是建立在 NVIDIA 私有库的基础之上。换到别家芯片,就算算子功能上等价,性能也大概率会有差距,因为别家没有积累十几年的调优经验。

第三层是习惯级锁定,这一层最隐蔽也最难破。所有 AI 开发者从入门第一天用的就是nvidia-smi看显存,就是torch.cuda.is_available()判断环境,就是 CUDA 版本和 PyTorch 版本要严格匹配这套玩法。你习惯了这套工作流之后,换一个平台意味着所有肌肉记忆都要重来。我见过不少团队,嘴上喊着"要支持国产芯片",真到迁移那天,光是一个"怎么查看昇腾 NPU 的算子执行耗时"就能卡住一整天。

3. "国产替代"拆开来看,技术路径到底是什么

3.1 硬件层:昇腾的达芬奇架构和 CUDA 的根本差异

华为昇腾芯片用的是自研的达芬奇(Da Vinci)架构,它和 NVIDIA 的 Ampere/Hopper/Blackwell 架构在设计哲学上有本质区别。NVIDIA 的思路是大规模通用并行:几千个 CUDA core 齐刷刷发力,适合各种形态的并行计算。达芬奇架构的核心是 AI Core,每个 AI Core 内部有一个 Cube 单元专门做矩阵运算,配合 Vector 单元做向量运算,还有 Scalar 单元处理标量逻辑,属于典型的"面向 AI 计算做定制"的设计。

这个差异带来的直接后果是:CUDA 代码里那种"把问题拆成大量线程并行处理"的写法,在昇腾上不一定高效。昇腾的编程模型更强调"我要算矩阵,就直接喂给 Cube 单元,数据搬运走专用的缓冲区"。所以迁移不只是改语法,而是要重新理解硬件的工作方式。我刚开始接触 CANN 的时候,最大的不适应就在这里——我习惯了 CUDA 那种"线程块 + 共享内存"的心智模型,到了昇腾上得换成"token 流 + 数据搬运"的思维,这个转变需要一段时间适应。

3.2 软件层:CANN、MindSpore 和兼容层的三层结构

华为在软件层构建的体系,可以看成三层:

最底层是 CANN(Compute Architecture for Neural Networks),它的定位类似于 CUDA 的驱动加运行时。CANN 屏蔽了底层硬件的差异,向上提供统一的算子开发接口和运行时管理能力,包括内存管理、流管理、算子调度这些基础服务。昇腾上跑的模型,最终都要落到 CANN 这一层。

中间层是框架适配层。华为自己的 MindSpore 是原生支持昇腾的,这也是为什么很多国产化项目直接选 MindSpore 的原因。但现实情况是,AI 社区的主流框架是 PyTorch,所以华为做了 PyTorch 的昇腾适配分支,让大部分 PyTorch 代码能通过替换后端的方式跑在昇腾上。这些年华为对 PyTorch 适配的投入明显加大,算子覆盖面越来越广,已经有不少模型能做到"改一两行配置就跑起来"。

最上层是模型层。DeepSeek 的适配工作主要发生在这里:把模型的算子实现、分布式策略、混合精度方案针对昇腾硬件重新设计和验证。这层工作说起来轻巧,实际工作量非常大。DeepSeek 这种大规模模型,训练时要考虑张量并行、流水线并行、数据并行多种策略的组合,每种并行策略对通信库的要求都不一样,而昇腾的集合通信库 HCCL 和 NVIDIA 的 NCCL 在接口语义和性能特性上都有差异,需要针对性地调。

3.3 DeepSeek 的角色:为什么模型方的适配这么关键

很多人会问:华为自己有 MindSpore,为什么还非得 DeepSeek 来适配?答案在于生态的"势能"。模型是 AI 生态里的"头部应用",一个明星模型选择跑在哪个平台,会直接决定开发者跟不跟。DeepSeek 的开源模型在技术社区的使用量极大,全球开发者每天用它在做推理、微调、二次开发。当这部分人发现"我用 DeepSeek 模型,可以直接在昇腾上跑,不需要 CUDA",那昇腾生态就不再是"HW 的生态",而是"所有 DeepSeek 用户的生态"。

这就是我常说的"应用反哺平台"的逻辑。芯片再强,没有头部应用在上面跑,开发者没有动力迁移;一旦有头部应用带头吃螃蟹,迁移的试错成本就大幅下降了。DeepSeek 做这件事的意义,比它单纯发布一个新模型版本要大得多——它等于在模型层给了整个生态一个"可复制的迁移范本"。

4. 从 CUDA 迁移到昇腾,实际操作要过哪些坎

4.1 迁移第一步:算子映射与重写

我实际帮团队做过一次模型迁移,把一套基于 PyTorch 的推荐模型从 CUDA 搬到昇腾上,整个过程走下来,第一个要面对的硬骨头就是算子映射。

PyTorch 模型里的每个操作——卷积、矩阵乘、LayerNorm、Softmax、Attention——在 CUDA 后端有一个实现,在昇腾后端是另一套实现。理想情况下,你只需要把model.to('cuda')改成model.to('npu'),剩下的框架帮你搞定。但现实往往没那么顺利:一些算子(比如某些自定义的 CUDA kernel)昇腾上没有对应的原生实现,要么用多个基础算子组合去模拟,要么就得自己写 TBE 算子——这是昇腾的自定义算子开发方式,类似于 CUDA kernel,但语法和优化思路完全不同。

我当时遇到的一个具体问题是模型里有一个自定义的融合算子,把一个 GEMM 和一个 elementwise 的激活函数融合在一起以减少显存读写。这在 CUDA 上是很成熟的优化手段,但昇腾的算子融合规则跟 CUDA 不一样,强行按原来的逻辑组合反而性能更差。最后我是把融合拆开,利用 CANN 提供的算子融合标记让它在底层自动融合,效果反而更好。这个经验说明了:迁移不是翻译,而是重新优化。

4.2 我踩过的坑:精度对齐、性能调优、Debug 工具链

第二个让我印象深刻的是精度对齐问题。同一套模型,在 CUDA 上跑和昇腾上跑,loss 曲线长得完全不同。这不是玄学,而是两边的浮点运算顺序、混合精度策略(FP16/BF16 的支持程度)、梯度累积方式都有细微差别。特别是在混合精度这块,NVIDIA 那边有成熟的 AMP(Automatic Mixed Precision)机制,昇腾上的混合精度实现逻辑不一样,某些算子在 FP16 下的精度表现差异会比较大,导致训练不稳定。

我当时花了一整周时间排查一个诡异的现象:模型在昇腾上 loss 早期降得飞快,但到了一定程度就再也不降了。后来定位到是某个归一化算子在低精度下的实现精度不够,换成 FP32 的计算路径就正常了,代价是那个算子稍微慢一点。这种问题在 CUDA 上几乎不会遇到,因为 NVIDIA 对每个算子的精度边界都做了严格验证,而昇腾的算子精度边界覆盖还有不少盲区。选型的时候一定要留出精度调优的时间预算。

第三个坑是Debug 工具链的成熟度差距。在 CUDA 上,跑崩了有 cuda-gdb,性能有问题有 ncu 可以做逐 kernel 分析,显存异常有 compute-sanitizer 查越界。昇腾这边也有对应的工具(比如 msprof、mindstudio),但说实话,流畅度和信息量跟 NVIDIA 工具链还有差距。特别是当你处理大规模分布式训练的时候,通信瓶颈的定位工具会直接影响你的排查效率。我的建议是:迁移团队的成员必须有一个是熟悉昇腾工具链的,否则遇到问题会非常被动。

4.3 哪些场景适合先迁移,哪些先观望

以我的经验,现在这个阶段,有几个场景非常适合先试水昇腾:

  • 推理场景。尤其是批量离线推理,对实时性要求不高、对吞吐量要求高,昇腾的推理性能和性价比已经比较能打了。而且推理链路相对干净,不涉及复杂的分布式训练策略。
  • 标准模型微调。比如用 DeepSeek-R1 做领域微调,数据量不大、模型结构改动不大,这类任务的迁移成本可控,适合作为团队的第一个昇腾项目。
  • 新项目冷启动。如果你正好要训一个新模型,没有历史包袱,那可以直接考虑以昇腾为主要训练平台,CUDA 作为备用对照。

反过来,暂时不建议迁移的场景包括:已有的超大规模预训练任务(迁移成本极高,且分布式通信优化需要大量投入)、重度依赖 NVIDIA 特定库(比如 TensorRT 的量化工具链)的项目、以及团队没有专职做底层优化的场景。对这些场景,更务实的策略是"双轨运行"——CUDA 继续作为主力,昇腾跑一个验证副本,逐步积累经验。

5. 对开发者来说,接下来该做什么准备

5.1 学习方法论:不一定要立刻换,但要开始懂

我这几年的体会是,技术选型最怕的不是选错,而是完全没准备。对于 CUDA 开发者,你不一定马上要切换到昇腾,但至少应该开始了解几件事:

第一,弄懂 CANN 的基本编程模型。不需要你马上写 TBE 算子,但至少要知道昇腾的 AI Core 怎么工作、数据搬运和计算是怎么流水线化的。这决定了你未来评估性能问题时能不能用对的分析框架。

第二,学会看昇腾的算子支持列表。PyTorch 在昇腾上的算子覆盖是持续扩展的,但总有一些边缘算子没覆盖到。你在设计模型结构的时候,如果能提前避开这些算子,迁移就会顺畅得多。我见过太多人把模型写完之后才发现某个自定义算子不支持,被迫重构网络结构。

第三,把通信库的差异放在心上。分布式训练是迁移中最容易被低估的部分。NCCL 的环形 AllReduce 和 HCCL 的实现差异,直接影响你在多卡训练时的扩展效率。建议找一份 HCCL 的文档通读一遍,重点看它支持的集合通信原语和拓扑感知策略。

5.2 给团队的建议:小步试错,双轨并行,留好回退

如果你是一个技术负责人,我的建议非常朴素:不要搞"大爆炸式迁移"。挑一个小模型、一条推理链路、一个非核心业务,用一两个迭代周期把它完整跑通,记录过程中的所有问题,形成团队的迁移知识库。然后再逐步扩大范围。

双轨并行的具体做法是:代码层面尽量做好硬件抽象,比如用 PyTorch 的 device 抽象,把cuda和npu处理成配置项,而不是写死在代码里。这样你在 CUDA 上开发的每一分积累,未来都有可能无缝地平移到昇腾上。我见过很多团队早期图省事,代码里写满了torch.cuda的硬编码,等要迁移的时候才发现改起来像拆地雷。

另外,一定要留好性能基准。迁移前,把模型在 CUDA 上的训练吞吐、推理延迟、显存占用全部记录成基准文档。迁移后逐项对比,性能差距可以接受和性能差异背后的原因要分开评估——前者是结论,后者是价值。有时候同样的模型在昇腾上慢,不是因为硬件不行,而是因为某个算子的实现方式不合理,换一种写法就能追平。

5.3 我在实际迁移中最后想分享的一点

文章写到这里,按照惯例应该做个总结,但我不太想写那种"展望未来"的套话。我更想说的是:这套迁移的事儿,最关键的不是技术本身,而是"开始动手"这件事。我见过太多讨论了,大家在群里聊得热火朝天,但真正去翻 CANN 文档、去跑第一个昇腾样例、去把自己的模型挪过去跑一遍的人少之又少。

我自己第一次把 PyTorch 模型搬到昇腾的体验,说实话并不愉快——环境配置就折腾了两天,各种版本不匹配、算子不支持的报错接踵而来。但跨过那个坎之后再回头看,我发现那些报错其实都是文档里写清楚的事情,只是我一开始没花时间去看。当你真正跑通第一个模型的那一刻,你会发现国产计算平台并没有想象中那么遥不可及。

最后分享一个小技巧:如果你打算开始尝试,不要一上来就搞大模型。找一个小巧的、你特别熟悉的模型,比如一个经典 CNN 或者在跑 DeepSeek 的蒸馏小模型,把迁移流程完整走一遍。这个过程里你能把 CANN 的算子映射逻辑、混合精度的设置方式、甚至调试工具链的用法都摸熟,之后再面对大项目就有了底气。技术选型的主动权,永远是留给那些提前动过手的人的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询