十月二十号,DeepSeek 把内部推理框架 DeepInfer 开源了,Apache-2.0。官方给的一句话是:它支撑着 DeepSeek V4.1 Flash 的大规模线上服务,单个集群目前一天服务 155 万亿 token,按谷时电价口径算,每张 GPU 每小时营收 38 美元。
我一开始以为这又是一个「又一个推理引擎」的发布。翻完 README、部署 Q&A 和整个源码目录之后,我的判断变了:这是跑在产线上的那套东西第一次露出内部结构,而不是一个拿来演示的仓库。它里面有几个决定,跟主流做法是反着来的。
不过先说最有意思的一点。这个仓库只开源了「一半」,而且是官方自己在 README 里写明的。我就从这一半说起。
先看它到底给了什么
README 里列的能力,我一条条对着源码目录核了一遍。仓库是 Python 为主,deepinfer/ 下面按引擎、模型执行、并行、调度切开;csrc/ 里是 C++ 的调度器和引擎部分;vendor/ 挂了一串子模块。
| 目录 | 里面是什么 |
|---|---|
csrc/engine/ |
调度器、序列、block manager、输入元数据准备(C++) |
deepinfer/model_executor/layers/tilelang_ops/ |
自研 TileLang 算子,cuda/ 和 asc/ 两套 |
deepinfer/model_executor/parallel_utils/ |
并行状态、通信算子、context parallel、NCCL 内存 |
deepinfer/model_executor/eplb/ |
专家并行负载均衡(策略 + 重排) |
deepinfer/model_executor/models/v4_modules/ |
V4 的模块:MLA 注意力、MoE、Engram、YoCo、MTP |
deepinfer/structure_output/ |
结构化输出(编译器、掩码、匹配器) |
deepinfer/worker/ |
CUDA graph、模型运行、cache engine、decode 性能缓存 |
vendor/ |
DeepEP、DeepEP-Ascend、DeepGEMM、DeepGEMM-Ascend、DeepSelect、FlashMLA |
deep_cluster/ |
硬件抽象(NVIDIA / Ascend / CPU)+ 拓扑绑定 |
支持的硬件写得很干脆:NVIDIA SM100,和华为昇腾 950。构建环境是 GCC 13 以上、Python 3.12、CUDA 13,昇腾那边要 CANN 9.2。
内核:拼装加自研,双平台落在算子层
DeepInfer 没有从零写一套算子库。它把 DeepSeek 自己这几年陆续开源的东西拼了进来:FlashMLA(注意力)、DeepGEMM(矩阵乘)、DeepEP(专家并行通信)、DeepSelect、再加一个 TileKernels,最后补上一批自研的 TileLang 算子。
这一点本身不新鲜。真正值得看的是那批 TileLang 算子怎么组织的。tilelang_ops/ 下面,每一个算子都有 cuda/ 和 asc/ 两个目录,同名文件,各写一份。比如 store_kv_cache.py、gather.py、fused_sample.py、structure_output.py,都是两套实现。
这说明「支持多家加速器」这句话,在这个仓库里是落在算子级的,不是加一层接口适配。昇腾那套实现里还有些 NVIDIA 版没有的文件,像是 rstd_1ulp.py、prepare_cp_mirror.py、sum_smaller_probs.py,看名字就是在处理数值精度和通信镜像的差异。数值精度这件事在跨平台时最容易出问题,它单独立了文件,说明是真踩过。
顺手说一个被忽略的组件:DeepSeek Recipe
跟 DeepInfer 一起开源的还有一个 deepseek-recipe,官方叫它「生产级 token 编解码组件」。这个仓库是 Rust 写的,MIT 协议,九月十号就建好了,现在一千五百多个 star。
它的职责是转换:一边是主流 LLM API 的协议,一边是模型的 token 序列。官方讲了两点设计。第一,它按特殊 token 分段,再把各段并行编码,好处是既防止特殊 token 被注入,又降低长输入的编码延迟。第二,它的处理逻辑跟 DeepSeek 官方 API 完全对齐,第三方自建部署能拿到和官方一致的 API 行为。
我觉得第二点比第一点更值钱。自建推理最烦的坑之一,就是官方 API 和本地部署的行为对不上:同一个请求,云端和本地返回的结构、边界处理不一样,你的上层代码就得写两套兼容逻辑。这东西把这块补齐了,是那种不显眼、但能省掉一堆调试时间的组件。
并行:它反直觉地不用 TP,也不用 CP
这是整篇里我觉得最值得写的一节。
部署 Q&A 里有一段自问自答,问「用了什么并行」,回答是这样的:Prefill 用 EP2–8 / DP2–8,取 HBM 允许的最小值;Decode 用 EP64–128 / DP64–128,叫 Wide-EP,取 GPU 允许的最大值;视觉编码器一个 GPU 一个实例就够,建议用便宜的卡。最后一句是重点:「TP 和 CP 目前不用。」
主流的推理框架里,张量并行(TP)几乎是默认选项。DeepInfer 把它拿掉了。为什么?因为 V4.1 Flash 是 MoE 模型,MoE 的瓶颈不在单层的矩阵乘规模,在专家路由之后每个 token 要走多少张卡、每步要搬多少显存。把 EP 开大,专家的权重摊到更多卡上,每一步的显存流量就被摊薄,吞吐自然上去。TP 在这个结构里只会引入额外的通信,不解决真问题。
官方放了一张 EP8 对比 EP64 的 decode 吞吐图来证明这件事(doc/images/wide_ep_decode_pareto.png)。同一批 GPU,EP 从 8 开到 64,吞吐明显上台阶。
还有一点:注意力部分用数据并行,而且是 #DP = #EP。这是从 DeepSeek-V3 那一代推理系统就定下来的结构。这个约束很省事,但也意味着负载均衡必须自己做,否则某一组 DP 副本忙死、另一组闲着。所以仓库里出现了四个负载均衡器:Prefill LB、Decode LB、视觉编码器 LB、以及专家并行的 EPLB。EPLB 的代码在 model_executor/eplb/ 里,就两个文件,一个策略一个重排。
为什么需要它?MoE 的专家是有冷热的。热门专家被路由到的 token 多,它所在的那张卡就成了木桶最短的那块板。EPLB 做的事就是定期统计每个专家的实际负载,把冷门的专家挪到空闲的卡上,让每张卡的负载尽量平。仓库里给它配了测试(tests/models/test_eplb_policy.py),还有专家重排的分布式测试,说明这是个会动生产配置的组件,不敢马虎。
确定性推理:两个开关,加一堆等价性测试
这一节对做 agent 的人最有用,也最容易被跳过。
README 里有两个功能挨着放,我第一遍看差点没注意。
一个是批不变性,英文写 batch-invariant。意思是同一份输入,不管它跟谁拼在同一个 batch 里,模型结果都一样。开法是一个环境变量,DEEPINFER_BATCH_INVARIANT=1,括号里注明只在 NVIDIA 上有效。
另一个是确定性采样。同样的输入 token 和采样参数,产出同样的结果。它要求你先开批不变性,再把 MTP 变长关掉(DEEPINFER_ENABLE_MTP_VARLEN=0),最后给采样参数传一个固定的 seq_seed。同样写着 NVIDIA only。
为什么这两件事难?因为推理服务的默认行为恰好是相反的。batch 里拼了谁、KV cache 分页落在哪、reduction 的累加顺序是什么,都会让浮点结果差出最后几位,然后采样一放大,输出就分叉了。要做到批不变,等于要求每个算子对 batch 组成不敏感,累加树固定、分页映射固定、通信顺序固定。
举个具体的例子。同一段 prompt,单独跑一次,和跟另外九段拼成一个 batch 跑一次,数学上算的是同一件事,但 GPU 上的规约树不一样:十路规约和一路规约的加法顺序不同,而浮点加法不满足结合律,最后几位就会差。这几位本身无关紧要,可它进了采样器的概率分布,温度一调、随机种子一滚,输出就可能分成两条路。
仓库里有一批测试专门钉这件事:tests/models/test_batch_invariant.py、test_ve_batch_invariant.py,还有 examples/yoco/yoco_equiv.py 配一个 L2 等价性脚本,以及一个单独检查 CUDA 同步的 test_cuda_sync_guards.py。做推理优化的人都知道,为了速度塞进去的异步和乱序,正是确定性最大的敌人,所以得有一道闸门看着它。
我一直觉得,推理的不确定性是 agent 复现问题的一个隐形来源:同样的 prompt 跑两遍结果不同,你很难判断是模型的问题还是你自己的代码的问题。有批不变性和确定性采样这两个开关,至少能把变量按住。
几个专有名词,顺手拆开
README 里还有一串名字,我按自己的理解拆一下,方便你读官方文档。
DSpark 和 confidence-scheduled verification。这是它的推测解码方案。用一个草稿模型(或者 MTP 头)先吐几个 token,再用置信度估计决定这一批草稿验不验、验多长。开关是 DEEPINFER_ENABLE_MTP_VARLEN=1,草稿长度上限由 DEEPINFER_USE_NEXTN_DECODE 控制,默认 5。
SWA bounded replay。滑动窗口注意力的 prefill 截断。它不重放整段上下文,只重放一个有界的后缀,官方给的默认值是把保留后缀设成 140 个 token(DEEPINFER_YOCO_PREFILL_KEEP_TOKENS=140)。前提是这个后缀得覆盖住 SWA 窗口加上最大回溯长度,覆盖不住结果就错了。
异步调度。把 CPU 侧的 batch 调度和设备执行重叠起来,官方叫它「零开销 batch scheduler」,默认开着。这类优化在压测里能榨出几个点,在生产里能决定尾延迟。
结构化输出。把生成约束在正则、JSON schema 或 EBNF 语法上,通过采样参数的 so_args 传进去。做 agent 工具调用的会关心这个,因为它决定了模型能不能稳定吐出可解析的结构。
那「一半」是什么,官方自己说了
翻到 README 底下,有一段标题叫「跑一个完整推理服务还需要什么(本次发布不含)」,列了三样:
- 一个 KV cache 系统;
- 视觉编码器 / Prefill / Decode 的解耦部署(大部分引擎功能已经实现);
- 在数据并行副本之间的负载均衡,prefill 和 decode 都要。
部署 Q&A 里把原因说得更直白:KV cache 管理和负载均衡这两块,跟 DeepSeek 内部的基础设施耦合太紧,所以那部分代码还没完全开源。
于是这个仓库的形态就清楚了:引擎开源了,架构写在了文档里。 代码给的是内核、模型执行、调度、采样这些「跑得动」的部分;而 KV cache 的存储与淘汰、跨副本的负载均衡,这些「跑得久」的部分只给了设计描述。
Q&A 里的设计描述给得相当具体。prefill 和 decode 可以跨数据中心部署,理由是 V4.1 Flash 把 KV cache 压得足够小,代价是可靠性,每 8 张卡大约要 400 Gbps 的互联带宽。存储服务器的推荐配置是每台 20 块 30.73 TB 的 SSD,读至少 40 GB/s、写至少 5 GB/s,容量要能留住 24 小时的数据。而它的设计目标之一是 KV cache 保留超过 72 小时,为的是缓存命中时不用重算 prefill。最小的高效部署单元是 64 卡 prefill 加 64 卡 decode,实际 P 和 D 的比例差不多 1:1。
我数了一下,这三样里最贵的就是没给的那两样。KV cache 保留 72 小时的存储成本和淘汰策略,跨副本负载均衡的调度逻辑,恰恰是长跑里决定成本的地方。所以「只开源一半」这件事,与其说是藏私,不如说这两块本来就长在它自己的机房拓扑上,抽出来给别人也未必能用。
它和 vLLM 的关系,值得单独说一句
README 最后有一段致谢,写得很坦白:DeepInfer 最初主要基于 vLLM 构建,另外借鉴了 SGLang 和 TensorRT-LLM。
这句话解释了这件事的性质。一个云厂商把自己线上的框架开源,底座是开源社区的项目,它自己加上去的是内核实现、并行配置、调度策略和确定性保障。对 vLLM 生态来说,这是一次大厂从下游往上回馈的样本;对想照抄的人来说,它给的是「一套被真实流量验证过的取舍」,而不是一个从零写的新引擎。
仓库状态也说一下,免得你把它当成一个成熟项目。这个仓库是十月十六号建的,比公开发布早四天,现在大约一万三千 star、一千二百 fork、一百三十二个 open issue,十月二十号代码还在推。刚开源四天的仓库有一百多个 issue,这个数字本身不说明质量,只能说明关注度高。
我看完之后的三个判断
第一,双平台的含金量在算子层,不在口号。每算子两套实现这件事,比任何「兼容多家芯片」的新闻稿都有说服力。它也解释了为什么 TileLang 值得 DeepSeek 单独开源一次,这套框架的跨平台能力就是建立在它上面的(前面写过它给昇腾做的工具链,可以一起看)。
第二,不用 TP 是个信号。它说明在 MoE 模型上,瓶颈已经从「单层算得多大」转到「专家权重怎么摊、每步搬多少显存」。跟着这个判断,推理框架的优化重点会继续往通信和调度上走,而不是往单卡算力上堆。
第三,确定性推理这两个开关,是这套框架里对 agent 生态最实用的东西,却几乎没人讨论。大家都在看 155 万亿 token 这个数字,但真正会影响你做 agent 评测和复现的,是那个 DEEPINFER_BATCH_INVARIANT=1。
想自己跑一遍的话,门槛在哪
构建环境是明确的:Linux、GCC 13 以上、Python 3.12,NVIDIA 那边要 CUDA 13,昇腾那边要 CANN 9.2。仓库里有个 build.sh,构建前要先激活一个干净的 Python 3.12 环境,再指定 DEEPINFER_ACCELERATOR 是 nvidia 还是 ascend。
跑起来倒是简单,examples/offline_inference.py 一行命令,默认会去 Hugging Face 拉 DeepSeek-V4.1-Flash-1017 的权重,想用本地副本就设 DEEPINFER_HF_MODEL_DIR。
但要复现它的性能曲线就是另一回事了。官方那张 prefill 图是四张 SM100 测的,decode 图是六十四张,而且强调都在同一个 scale-up 域内。也就是说,你至少得有几十张卡,才有资格验证它的吞吐声明。
写在最后
DeepSeek 这次的发布,我觉得最值得记的一件事,是它用一份 README 把「一个完整推理服务」和「一个推理引擎」的边界划清楚了。它给了你引擎,也明确告诉你缺哪两块,还把缺的那两块的设计思路写在了 Q&A 里。这种交代方式,比放一堆跑分图有用得多。
至于要不要用:如果你只是想跑通 DeepSeek V4.1 Flash,这个仓库加上它的模型卡就够了;如果你要真上生产,得先想清楚 KV cache 和负载均衡自己怎么补,因为这两块决定了你的成本曲线,而它们不在代码里。
相关阅读:
- 《Agent 推理延迟优化:从首 Token 到流式输出的四层战场》:KV Cache、推测解码、批处理这些概念,那篇有从工程角度的完整梳理,可以跟 DeepInfer 的实现对照看。
- 《DeepSeek 开源华为 Ascend 编程工具链!TileLang 剑指 CUDA》:DeepInfer 的跨平台能力就建在 TileLang 上,那篇讲了工具链本身。
- 《DeepSeek V4.1 Flash 来了!》:DeepInfer 服务的就是这个模型,先看模型再看框架,顺序更顺。