← 返回观点 思考

DeepSeek 迁移昇腾,究竟在迁移什么?

把迁移理解为「加载权重、跑通生成」,解释不了这批开源为什么存在。一次推理先组批,再逐层计算:层内先由索引器挑出上下文位置、稀疏注意力按索引读取 KV,再经路由、dispatch、分组 GEMM 和 combine 完成专家计算;API 兼容之外,缩放因子排布、内核流水与同步仍要针对昇腾重新适配。本文先算清两本账(每 token 激活 37B,全服务常驻 671B),划出可复用与必须重做的边界。

2026-10-08思考37 分钟阅读
DeepSeek 迁移昇腾,究竟在迁移什么?

——从「模型能运行」到「完整计算路径高效执行」

摘要:如果把迁移理解为「加载权重、跑通生成」,就解释不了这批开源为什么存在。一次推理先组批,再逐层计算。在层内,先由索引器挑出上下文位置,稀疏注意力再按索引读取 KV;之后经过路由、dispatch、分组 GEMM 和 combine,完成专家计算。各组件之间按对齐与布局约定交换数据。API 兼容之外,缩放因子排布、内核流水与同步仍然要针对昇腾重新适配。本文先算清两本账(每个 token 激活 37B,整个服务常驻 671B),再划出可复用与必须重做的边界。迁移真正要做的,是重新安排计算、数据与资源之间的关系。

2026 年 9 月 30 日,DeepSeek 集中开源了一批面向华为昇腾(Ascend)的推理组件,包括:

  • 矩阵乘法内核库 DeepGEMM-Ascend;
  • 专家并行通信库 DeepEP-Ascend;
  • 稀疏注意力算子 FlashMLA;
  • 辅助算子库 TileKernels;
  • 负责内核编译与加载的 DeepJIT。

这些项目此前都有 NVIDIA 版本。这批代码公开后,推理软件栈的主要环节第一次在两个平台上同时有了公开实现。覆盖范围仍有边界:FlashMLA 的昇腾支持目前只有稀疏注意力算子,融合 kernel 与 dense attention 仍标注为仅支持 CUDA(FlashMLA README)。不过主体已经齐备。

问题也随之而来:DeepSeek 的模型本来就能在昇腾上运行,为什么还要专门开源这一整套矩阵乘法、注意力、通信和编译运行时?

答案在于两件事的区别:模型定义「要算什么」,软件栈决定「这些计算怎样落到硬件上」。「能输出文字」只能证明存在一条走得通的计算路径。这条路走得快不快是另一个问题,而部署成本恰恰由它决定。本篇先把系统整体摊开,给出「什么可以复用、什么必须重做」的判断框架。后续六篇依次讨论数据布局与矩阵形状、专家并行通信、注意力内核、正确性验证、经济成本和生态可持续性。


一、先把系统搭起来:一次推理要经历什么

推理服务收到请求后,第一件事是组批:把多个请求的输入拼成一个批次,交给模型逐层计算。这里有一个容易被忽略的事实:prefill 和 decode 两个阶段做的事完全不同。

  • Prefill 一次读入整段输入,每个请求贡献一大段连续位置,读取权重的开销被大量位置分摊。它还要顺带生成 KV 缓存,也就是「已处理上下文」的中间数据,供后续步骤反复使用。
  • Decode 每步只为每个请求生成一个 token,新参与计算的位置很少,却要按整段上下文重新读一遍 KV。

两个阶段的数据复用结构和读写比例截然不同。另外,批次的实际规模还取决于并发请求数、输入长度和切分方式,不能从「每个请求每步一个 token」直接推出整批的形状。第四节会给出同一个索引内核在两种查询规模下的官方成绩,形状差异带来的变化值得细看。

进入层内部,以一个含 MoE 的典型层为例:注意力段决定「每个位置看哪些上下文」,MoE 段决定「每个 token 交给哪些专家计算」。整套服务要运转起来,软件栈需要在四个层面各管一摊:

  • 推理框架负责组批、调度和 KV 缓存;
  • 算子库负责每段计算的具体执行;
  • 通信库负责 token 在设备之间的往返;
  • 编译运行时负责把内核源码变成可加载的设备二进制。

这次开源覆盖的是后三层的主要环节。

把层内的两条路径串起来,五个仓库的位置就清楚了:

  • 注意力段:索引器调用 DeepGEMM 的 MQA logits 算子,为每个查询给各个上下文位置打分,据此选出位置、生成索引表。FlashMLA 按索引表稀疏读取 KV,完成注意力计算。索引表的格式与 KV 的存放布局,是这两个仓库之间共同遵守的一份契约。
  • MoE 段:TileKernels 的路由算子为每个 token 选出专家;DeepEP 的 dispatch 把输入发到专家所在的设备;DeepGEMM 的分组 GEMM 完成专家计算,路由权重在这一步应用一次;DeepEP 的 combine 再把已加权的结果归并回原请求。

组批之后,一个请求主要沿这两条路径流经五个仓库。说「主要」,是因为框架层的调度和其他算子也在路径上,本篇只展开这次开源的重点。编译方面,DeepJIT 负责 DeepGEMM 与 DeepEP 中手写内核的编译、缓存和加载;TileKernels 走 TileLang 路线,DeepGEMM 中也有部分 TileLang 实现。两条工具链怎样分工,第五节再展开。

图 1 系统关系全景:服务层的组批与 prefill/decode 两阶段,层内的索引/注意力与 MoE 两条路径,跨仓库契约,以及 DeepJIT 编译底座
图 1 系统关系全景:服务层的组批与 prefill/decode 两阶段,层内的索引/注意力与 MoE 两条路径,跨仓库契约,以及 DeepJIT 编译底座

这幅全景图还需要补充一个背景:DeepSeek 模型并不是此前无法在昇腾上运行。V3.2-Exp 的发布材料就已经提供了 SGLang 的 NPU 镜像入口。这次公开的是另一回事:面向昇腾 950 的原生高性能内核及其公共接口成了公开可读的实现,迁移团队可以直接研究、直接复用这些底层优化。至于各框架版本实际采用了哪些新内核,公开材料没有说明。有镜像入口,也不等于有了一套经过验证的新运行栈,框架层的集成与验收仍要使用者自己完成。这个区分贯穿整个系列。


二、资源结构:每个 token 的账,与整个服务的账

理解 MoE 部署,先要算清两本账。以 DeepSeek-V3 为例:总参数 671B,每个 token 激活 37B(DeepSeek-V3 仓库)。这里引用 V3 只是为了说明资源结构,FlashMLA 的最新版本面向的已经是 V4.1。

先做一个只考虑参数量与存储精度的粗算:671 × 10⁹ 个参数 × 2 bytes ≈ 1.34 TB。这个数字假设全部参数按两字节存储,不包括 KV 缓存、运行时工作区和通信缓冲,也不等于官方量化检查点的实际体积——官方发布的 V3 权重以 FP8 为主,实际检查点比这个数小;按两字节算,得到的是一个偏保守的上限。它的作用只是把「总参数量」换算成部署时要面对的第一个数字。

仅这一步,就足以看出两本账的差别:

每个 token 参与计算的参数量,和整个服务必须容纳的参数量,是两本不同的账。 37B 衡量的是单个 token 前向计算要经过多少参数,但它不能单独决定执行成本,因为注意力部分的计算量还会随上下文长度变化。按上述两字节假设,1.34 TB 才是全部权重的存储体积,它决定了多台设备要在内存里共同装下多少东西。

在专家权重常驻设备、由多台机器分担的部署中,即使一次前向只用到少数几个专家,系统也必须一直回答四个问题:

  1. 放置:所有专家放在哪里;
  2. 路由:当前 token 送到哪里;
  3. 负载观测与再平衡:哪些设备承担了更多的专家计算;
  4. 归并与同步:专家的计算结果怎样回到原请求。

后续各篇,分别回答其中的某一问。

图 2 两本账与内存预算:每个 token 激活的 37B 参数,与全服务常驻的 671B(按 ×2 bytes 推算约 1.34 TB 权重);下方为内存预算的五项分解
图 2 两本账与内存预算:每个 token 激活的 37B 参数,与全服务常驻的 671B(按 ×2 bytes 推算约 1.34 TB 权重);下方为内存预算的五项分解

把这四个问题涉及的对象写成预算,服务所需的内存可以分解为:

M_总 = M_权重 + M_KV + M_工作区 + M_通信 + M_预留

这是一个核算框架,不是某个推理框架的精确公式。它的用处是把「某个内核被优化了」换成更具体的问题:这项优化动了预算里的哪一项,挤占或释放了什么。在内存容量受限的部署中,省时间的优化和省内存的优化价值并不相同。后面每一篇都会反复用到这个框架。


三、一个 MoE 层的完整往返:接口在哪里,融合在哪里

两本账决定了服务要装下什么。本节沿 MoE 路径往下走,看五个仓库的接口怎样衔接。这是全篇跨仓库耦合最深的一段。

起点是路由。 TileKernels 提供 top-k 专家选择与打分算子,输出每个 token 对应的专家编号和权重。它还包含量化算子(per-token / per-block 的 FP8/FP4 转换、融合 SwiGLU 的量化),以及 mHC、Engram 等算子。这些算子全部用多后端领域语言 TileLang 编写,运行时自动选择硬件后端(TileKernels README)。

接着是 dispatch。 路由结果交给 DeepEP-Ascend 的 dispatch,由它把每个 token 的输入按专家分组,发往各个设备。这里有几条接口约束看上去像「细节」,实际上决定了工程的整体形态:

  • 通信不是临时发起的,而是围绕一个长生命周期的 EPBuffer 来组织。每个专家并行组创建一个 EPBuffer,跨层、跨 microbatch 复用;所有 rank 必须对 token 容量达成一致;调整大小、复用或销毁之前,必须先完成所有未完成的操作。
  • dispatch 的对齐参数必须与分组矩阵乘的 token 对齐粒度一致。通信库按这个粒度组织发送的行数和零填充,下游的分组 GEMM 才能直接使用收到的数据。

也就是说,通信库和计算库之间不是只传字节的松耦合管道,两边要按同一份对齐约定分别实现。发送前的展开与零填充、专家侧的设备端计数等接口选项,都是这条路径上的固定步骤,第三篇会逐项展开(DeepEP README)。

到了专家侧就是计算。 DeepGEMM-Ascend 的分组 GEMM 按每个专家收到的行数组织矩阵乘,一个专家网络就是两次分组 GEMM 中间夹一个 SwiGLU。按 README 的标准示例,专家计算会把收到的路由权重应用一次,输出 BF16 结果。随后,DeepEP 的 combine 把结果归并回原请求。归并的是已经加权的结果,不会再乘一次权重。

README 给出的标准用法,本身就是一套通信与计算交错进行的调度:先发起 dispatch,在等待期间计算与它无关的部分(示例中是共享专家),再通过 wait 取回收到的输入;专家计算完成后发起 combine,同时把共享专家的输出作为 bias 一起归并。可见通信与计算之间不是简单的「先通信、后计算」,而是一组可以交错执行的操作,由 handle(记录分发结构的凭据)和 wait 的时机显式管理。

融合是另一种选择。 第一节的全景图按功能把通信和计算分列在不同组件中,但这些阶段也可以合成一个算子执行。最直接的例子是 DeepGEMM-Ascend 的 MegaMoE,它把 EP dispatch、两次分组 GEMM、SwiGLU 和 combine 全部放进同一个算子。官方基准在 EP8、top-6 路由加一个共享专家的配置下,给出了整条路径的时延、计算吞吐、访存带宽和通信带宽。以 384 个专家、隐藏维度 7168、16384 个 token 这一档为例,计算吞吐为 846 TFLOPS,通信带宽为 103.3 GB/s(DeepGEMM README)。

README 对融合的说明是:这几个阶段在同一个算子内组织和调度。阶段之间的数据依赖、通信缓冲和同步依然存在,只是改由算子内部处理,不再需要上层逐段衔接。

对迁移方来说,这需要权衡:一种做法是 dispatch、专家计算、combine 分段调用,每一段都能单独替换、单独安排;另一种是采用融合算子,整段路径一次调用完成。


四、注意力层的两条路:索引由谁计算

注意力段的计算同样跨越多个仓库。DeepSeek Sparse Attention(DSA)把注意力拆成两步:先通过索引计算找出值得关注的上下文位置,再由稀疏注意力内核只对选中的位置计算注意力(DeepSeek-V3.2-Exp)。V3.2-Exp 的发布说明写得很明确:高性能索引打分内核(含分页版本)在 DeepGEMM 中,稀疏注意力内核在 FlashMLA 中。

可见 DeepGEMM 不只服务于 MoE。它的 MQA logits 算子就是 Lightning Indexer 的打分内核。README 明确指出,这个内核是 FIX-pipe bound:瓶颈在计算结果搬出的通路上,不在矩阵计算本身,FIX 管线利用率达到 99%(README 对该内核的整体说明,未区分查询档位)。基准同时给出了两种查询规模下的成绩(FP8):

档位 查询 token 数 KV token 数 头数 × 维度 计算吞吐 带宽
prefill 4096 8192 32 × 128 681 TFLOPS 283 GB/s
decode 256 8192 32 × 128 565 TFLOPS 2006 GB/s

注意这两组数字的变化:查询规模缩小到 1/16(4096→256),计算吞吐只下降约 17%,统计带宽却升高到约 7.1 倍。同一个内核换了形状,两栏数字一起变化。但按 README 的判断,两种形状下都是 FIX-pipe bound,瓶颈类型没有随阶段改变。

FlashMLA 使用的是索引表:它按索引稀疏读取 KV,在片上用在线 softmax 逐步累积输出。它的昇腾实现为什么能达到 410/360 TFLOPS(分别为峰值的 95%/83%),是第四篇的主题。这里只需要看清系统层面的含义:注意力优化跨越两个仓库,还要加上一份布局契约。索引格式、KV 存放布局、量化缩放因子的排布,都必须在生产数据的一方和使用数据的一方之间对齐。迁移中的接口工作,大量发生在这种「仓库之间」,而不是仓库内部。


五、编译运行时:一条公共底座,但不是唯一一条

五个仓库之下还有一层结构,很容易被误读。

DeepJIT 是 DeepGEMM 与 DeepEP 共用的即时编译底座,负责在运行时编译、缓存、加载并启动内核,CUDA 和昇腾使用同一套接口。昇腾侧的流程是:用 Bisheng 编译,用 ld.lld 链接,再经 ACL 加载。缓存键涵盖源码、被跟踪的头文件、编译器版本、实际生效的编译选项,以及调用方显式提供的依赖签名。缓存目录可以在不同用户、进程乃至节点之间共享。两个库各有独立的环境变量前缀,没有设置时回退到全局前缀(细节在第五篇正确性验证里展开)。也就是说,在公共底座之上,每个库仍然保留自己的编译策略(DeepJIT README)。

但如果由此认为「编译运行时已经统一」,那就错了。TileKernels 的全部算子走 TileLang 工具链,运行时自动选择后端;就连 DeepGEMM 自己的 mHC prenorm 内核,也是用 TileLang 写的。换句话说,同一批开源组件里并存两条编译路线:DeepJIT 负责手写内核源码的即时编译,TileLang 负责从领域语言生成多个后端的代码。

对迁移方来说,工具链不能一刀切,每个组件都要按自己的路线配置编译、缓存和版本管理。第五篇(正确性验证)会进一步说明,缓存键的设计会怎样影响「你以为自己在运行的代码」。


六、接口对齐不等于能力对齐

「与 NVIDIA 版 API 对齐」是这批项目共同的卖点。但对齐的是接口形状,不是能力清单。DeepEP-Ascend 的支持现状可以整理成下表(DeepEP README):

状态 内容
可用(带约束) EP dispatch/combine:已实现,但使用时要满足接口和环境条件(部分参数取固定值,路由索引固定为 int64;细则见第三篇)
开发中 Bucket:批量 all-gather 可用,reduce-scatter 与 all-reduce 内核仍在开发;专家负载均衡接口 lb_prefetch_weights / lb_reduce_grads 只对齐了 API,昇腾通信内核尚未实现
实验阶段 PP 与 Engram:正确性与性能验证仍在进行
不支持 混合通信、CPU 侧 Engram 存储、图捕获(graph capture)

DeepGEMM-Ascend 同样是「API 形状相同,内部重新安排」。函数签名沿用 NVIDIA 版,但缩放因子在昇腾上的排布不同:UE8M0 两两打包进 int16,采用 MN-major。仓库为此提供了专门的布局转换工具。此外还有按计算核数限制并发的选项、确定性算法开关,以及在对应 GEMM 接口上与 aclnn 参考实现交叉验证的测试(DeepGEMM README)。FlashMLA 的昇腾支持范围如开篇所述,只覆盖稀疏注意力。

运行环境也有差异。DeepEP 公布成绩时用的是手工配置的 PoC HDK,不是公开发行版。根据 README 的说法,商用版本预计在 10 月中旬由厂商发布。在它公开之前,「代码可以读」和「成绩可以复现」之间,还差一个运行环境。第七篇会把这一点放进生态判断中讨论。

把这些事实放在一起,就能划出清晰的分界:

  • 可以复用的:接口形状、缓冲区抽象、算法设计和工程知识;
  • 必须重做或等待的:各后端的内核实现、数据布局、流水安排、同步语义,以及清单中那些「API 已有、内核未到」的缺口。

七、这批开源价值几何:判断与边界

现在可以给出整体判断。

直接价值在于,推理路径的主要环节在两个平台上都有了可以研究的公开实现和官方成绩。密集 GEMM 的最高标称值达到硬件上限的 99.8%,稀疏注意力达到峰值的 95%/83%,EP 通信给出了五档规模下的带宽,MegaMoE 连融合路径都有整段基准(各项成绩的出处与测试条件,见第二、三、四篇的官方基准表)。对部署方来说,官方基准为每个环节提供了部署后可以对照核验的参考值,不仅能判断「能不能跑」,还能检查「跑到了什么水平」。对做优化的人来说,每个环节都有了可以拆开研究的公开实现。

但算子不等于服务。 五个仓库本身构不成完整的服务,局部基准也替代不了实际框架组合下的验收。接口清单里还有明确的缺口:负载均衡通信内核没有实现,图捕获不支持,PP 与 Engram 仍处于实验阶段,取得成绩所用的环境也还没有公开发行。这些缺口决定了「替换」的实际工作量。上层调用和工程知识可以迁移复用;数据布局(例如缩放因子排布)、内核内部的搬运与计算流水、跨仓库的同步语义,则需要按昇腾的实现重做,或者等相应能力补齐。

回到标题的问题:迁移不是逐个替换函数,而是重新安排计算、数据与资源之间的关系。 模型定义的计算图只有一张,但它在两种硬件上的高效执行方式各不相同,具体体现在三个方面:

  • 哪些计算要合并:MegaMoE 把整条 MoE 路径放进一个算子;
  • 哪些接口契约要跨仓库共享:对齐粒度同时约束通信和计算,索引与 KV 布局跨越两个仓库;
  • 哪些等待值得用重叠来隐藏:dispatch 与共享专家计算交错进行。

落实到工程清单上,就是三件事:

  1. 上层接口和调用知识,直接复用;
  2. 布局、流水和同步,按昇腾的语义逐项重做,或者等待能力补齐;
  3. 组合起来的整条路径,在实际框架中自己验证。

后面六篇,会把这些内容逐层展开。


资料与计算说明:本文依据五个项目的公开仓库与文档撰写,资料核验截至 2026 年 10 月 7 日。正文引用的成绩、接口与能力清单均来自对应的官方 README;DSA 的两步拆分与 SGLang 镜像入口来自 V3.2-Exp 发布材料;DeepSeek-V3 的参数量(671B / 37B)来自官方仓库。1.34 TB 的估算(671 × 10⁹ × 2 bytes)和 M<sub>总</sub> 分解,是本文自己的推算和分析框架:假设全部参数按两字节存储,只计入主模型权重,不代表官方量化检查点的体积——官方以 FP8 权重为主,实际检查点更小,两字节估算是保守上限。商用 HDK 的时间(约 2026 年 10 月中旬)转述自 README,以厂商实际发布为准。本文没有做硬件实测。本文是「DeepSeek 昇腾开源系列」的第一篇。