DeepSeek 开源 DeepInfer:155 万亿 token/天的推理框架,和它没开出来的部分
10 月 20 日,DeepSeek 把自家线上服务用的推理框架开源了。仓库叫 DeepInfer,Apache-2.0,配套还放出一个协议编解码组件 deepseek-recipe。
这不是一个「能跑起来就行」的 demo 框架。官方在 README 里给的定位很直白:DeepInfer is the high-performance inference framework that powers DeepSeek’s large-scale production services——它是撑起 V4.1 Flash 线上服务的那套东西。同一天发布的部署问答里,官方给了两个硬数字:单个集群每天服务 155 万亿个 token,整体收益按谷时电价折算为每 GPU 小时 38 美元。
这两个数字叠在一起,其实暴露了推理框架这个赛道现在的真问题:模型能力决定上限,能不能把这种能力用合理的成本铺给海量用户,才是框架要解的事。本文把官方材料里可核的部分拆开讲,重点放在那些「别家博客不会写、但你自己部署时会踩」的地方。
这次开源的是什么,边界在哪
先把话说清楚,省得下载完发现缺件。
README 的 Supported features 一节列了八项:优化过的 kernel、模型与采样运行时、Attention DP / balanced CP 与 MoE EP + EPLB、DSpark 与 confidence-scheduled 验证、SWA bounded replay、异步调度(zero-overhead batch scheduler)、batch-invariant 推理、确定性采样、结构化输出。
支持模型只有一个:DeepSeek-V4.1-Flash。 硬件只支持两种平台:NVIDIA SM100(CUDA 13)与华为昇腾 950(CANN 9.2)。这不是通用的 vLLM 替代品,官方在致谢里也承认它「原本主要基于 vLLM 构建,另受 SGLang 与 TensorRT-LLM 启发」。
最关键的一节在 README 里只有四行标题,官方自己写的 not included in this release:
- 一套 KV cache 系统;
- VE / Prefill / Decode 解耦(大部分引擎侧功能已实现);
- prefill 与 decode 两侧跨 DP 副本的负载均衡。
部署问答里的措辞更直接:The KV cache management and load balancing components of the inference system are tightly coupled to our internal infrastructure, so that code is not yet fully open-sourced.——这两块和内部基础设施耦合太深,暂时没全开。
把这两段放一起读,开源边界就很清楚了:kernel 层与单机引擎层的功能是齐的,能离线跑推理;但要凑出一套「线上服务」,缺的恰恰是决定成本的那两块——缓存与调度。官方补了个知乎长文讲这两块的设计,代码本身没给。
如果你是冲着「开箱即用的 DeepSeek 推理服务」来的,这份仓库满足不了;如果目的是读它的实现细节、把自己的 vLLM 改得更像它,那价值就很高。
三个设计目标,和「155 万亿」这笔账
部署问答开头列了服务系统的三个起点目标,值得逐条看,因为它们解释了后面几乎每个设计选择:
- 支持多厂商加速器,并且要用满各平台的性能;
- 用商品化硬件组织 prefill 与 decode:prefill 用普通 8 卡机,decode 用普通 64–128 卡的超节点,计算/通信/调度都围绕两个阶段各自的负载特征来设计;
- KV cache 保留超过 72 小时,让上下文复用窗口更长,减少重复 prefill。
第二条里「普通 8 卡机」和「普通 64–128 卡超节点」这两个词很值得注意——它意味着整套设计是刻意避开定制互联、往通用硬件上压的。
关于 38 美元这个数,有个细节需要自己辨:官方给的是「单集群 155 万亿 token/天 + 每 GPU 小时 38 美元收益」两个孤立数字,没有给集群卡数,所以从这份材料里算不出单卡吞吐,也算不出每百万 token 的成本。任何转述里出现「每百万 token X 美元」的说法,都是从别处凑出来的,不是这份官方口径。
部署形状:E/P/D 解耦,加上一组专用存储节点
官方给的参考架构一句话概括:Vision Encoder / Prefill / Decode 三阶段解耦,外加专用存储节点。注意力部分沿用 V3/R1 那套(#DP = #EP),并行策略的取值官方写得很干脆:
- Prefill:EP2–8 / DP2–8,取 HBM 允许范围内最小的 EP/DP;
- Decode:EP64–128 / DP64–128(Wide-EP),取 GPU 允许范围内最大的 EP/DP;
- VE(视觉编码器):一个实例一张卡通常就够,官方明确建议用更便宜的卡;
- TP 与 CP 目前不用。
注意 prefill 要「最小」,decode 要「最大」,这不是随口的建议,是两阶段负载性质决定的:prefill 是计算密集且天然可以跨 DP 摊,EP 开大反而增加通信摊薄收益;decode 每步都要过一遍 MoE 专家权重,是访存瓶颈,EP 开大能显著降低每步的显存流量,再配合 EPLB 就能把 TPS 顶上去——官方给了一张 EP8 对 EP64 的 decode 吞吐对比图(离线 benchmark 脚本产出)。
最小部署单元这一问的答案很实用:1P + 1D 技术上可部署,实际 P
多机/多机房部署也给了口径:V4.1 Flash 对 KV cache 压缩得足够狠,把 P 和 D 分到不同机房在技术上可行(代价是可靠性,比如跨机房光纤容易被施工挖断),按每 8 张 GPU 约 400 Gbps 互联带宽预留。DeepSeek 自己的推理节点就部署在不同但相邻的机房里。
KV 缓存:三张介质表,和「留 72 小时」的真实代价
官方直接给了一张表,把 KV 缓存按介质分成三类,这张表基本定义了整套系统的成本结构:
| 内容 | 介质 | 副本 | 谁管 |
|---|---|---|---|
| 在途 block KV cache + state KV cache | 推理 GPU 显存 | — | prefill / decode 实例 |
| SWA KV cache | 本地主机内存 | 单副本,不上 3FS | prefill / decode 实例;跨机命中走 400G VLAN 拉取 |
| 分页 block KV cache | 专用 3FS 存储集群 | 无应用层副本,依赖 3FS 自身冗余 | kvcache-store |
为什么 block KV cache 要放专用存储集群,而不是本地内存做分层? 官方给的理由比「容量大」更实在:专用集群让每台 prefill 机器都能命中任意前缀,从而避免「前缀亲和性」和「负载均衡路由策略」打架;同时把存储节点的扩容与计算节点的扩容解耦,并且 KV 缓存不再受计算节点故障影响——计算节点的故障率比存储节点高一个数量级以上。这最后一点是很多人做分层缓存时会忽略的账。
存储节点的标准配置:每台 20 块 30.73 TB SSD,单机至少 40 GB/s 读、5 GB/s 写,容量要能留存 24 小时数据。存储集群与在线 GPU 的比例,官方换算成逻辑容量口径是每 GPU 约 14.4 TB(物理容量要更大)。
3FS 侧的两个细节值得记:数据按 10+2 纠删码存储,一个 EC 组能容忍两台节点同时故障;读请求会同时发给组内 11 个存储节点,任意 10 个响应就够解码返回。回收靠一个 GC Worker,每存储节点一个实例,按 LRU 定期淘汰,把磁盘占用压在水位线以下。
TTL 那条是最有信息量的一条。 官方说 kvcache-store 侧不做过期逻辑,回收完全交给 3FS 自己的 GC;3FS 给 KV 缓存约 100 PiB SSD,TTL 定为 agent 流量 3 天、chatbot 流量 7 天。为什么 chatbot 要给更长的 7 天?因为 chatbot 流量的命中分布有很重的长尾——少量 KV 缓存被反复读很多次,丢掉它的整体代价更高。官方给了实测:TTL 取 3 天,chatbot 流量的 KV 命中率要损失 0.5%–1%。
这句话反过来读就是一个部署建议:如果你在自建同构系统,TTL 不要凭感觉拍。先看你的流量长尾有多重,再拿命中率损失去换存储成本。
缓存的三个开销环节,官方的处理方式也很具体:匹配由 Entrypoint 对处于 CHECK 角色的本地 kvcache-store 做,是只在元数据上跑的哈希链前缀匹配——但命中长度必须在路由之前算出来,所以这一步没法隐藏;Fetch 在独立进程里异步做,把 KV 从 3FS 搬到主机共享内存;Swap-in 从共享内存进 HBM 在引擎进程内的线程里跑,目标是让逐层 H2D 拷贝与 forward 计算重叠。这里有个很实用的实现结论:因为 CUDA copy engine 在细粒度 KV block 拷贝上表现很差,swap-in 改用 GDR 实现——用 RDMA 网卡打满 PCIe 带宽,同时不占用 SM。decode 侧明确不做 KV offload,也没用 Hi-Sparse,理由是 decode 不受缓存容量约束;但 decode 产生的 KV 依然会写盘,只为提高命中率。
SWA 有界重放:把「重算」变成一个与上下文长度无关的常数
这是我读完最想推荐的一块,也是 DeepInfer 比较少见于其它框架的设计。
先看它存什么。 SWA KV 缓存不进分页 block,而是放在 state cache 里,每个序列占一个定长槽位(StateBlock),槽位被切成两段:[stored region: SWA] [dropped region: YOCO consumer SWA]。前一段是需要跨请求保留的滑动窗口;后一段放 YOCO consumer 层的 SWA 以及压缩边界累加器 ca_state / ica_state,它们的恢复靠 prefill 重算,所以不用存——官方特别说明压缩窗口边界天然对齐 128 token 的存储粒度,因此没有跨边界依赖。V4.1 里存的是前 20 层。
存储粒度是 128 token 的整数倍。 prefill 之后,存储位置是「序列末尾保留一个 token 再向下取整到 128 的倍数」;不足一个完整的 128 单元就这一轮不存。分块 prefill 中途不存,只有最后一个 chunk 存。decode 阶段则在显存里另开一块副本区,请求每跨过一次 128 token 边界就把当前窗口拷进副本区;请求结束时取副本区里最近一次完整拷贝作为存储内容。拷贝位置最多会越过边界 5 个 token,靠槽位环形缓冲比窗口多留的 6 个位置吸收。
具体体积官方直接给了公式:state_size_to_disk = 20 层 × 134 token × 528 B = 1,415,040 B/序列。528 B/token 就是 SWA KV 的 MXFP8 格式。这个数很关键——它意味着每个被复用的序列,其 SWA 状态只占约 1.35 MiB,而且不上持久化存储,就留在产出它的那台机器内存里;跨机命中依赖 Entrypoint 的内存目录(逻辑统一、物理按 key 分区到各 Entrypoint 机器)加 400G VLAN 拉回。
再看重算。 SWA bounded replay 的核心是:只重算最近 128 个 token,并把注意力窗口截断在这个重放段内——从位置 s 重放时,位置 i 的 query 只对 max(s, i−127) 到 i 的 key 计算,得到一个近似的 SWA KV 缓存。代价因此变成一个常数,与上下文长度、层数都无关。
它用在两个地方,第二个用法是真正的省钱点:
- 编码器侧:前缀命中长度现在只由 block KV cache 决定。SWA 缺失时,把命中前缀的最后 128 个 token 重算一遍,与未命中的后缀一起前推;重放段只重建 SWA KV,block KV 原样复用,既不重算也不覆盖。
- 解码器侧:prefill 计算只发生在 encoder 层。CED 架构下 decoder 层的 block KV 是从最后一层 encoder 的隐状态投影来的,所以只有 decoder 的 SWA KV 需要逐层计算——精确重建要跑最后 2,560 个 token 穿过 20 层 decoder。改成每次 prefill 只重算提示词最后 128 个 token,结果只用于 decode、永不进入前缀缓存,官方口径是几乎把 prefill 总计算量减半。
命中策略的规则也不绕:命中长度由 block KV cache 决定,最后一个 block 要不要重算由 SWA KV cache 决定——SWA 未命中就退一个 block 重算。在 YOCO 截断约束下,长度 L 的输入最大命中长度不超过 L − 134(128 窗口 + 6 个推测回溯 token)。在这个上限内先查 block KV 得到 L1(128 的整数倍),再查 SWA:若 L1 − 128 到 L1 区间内有 SWA 缓存则命长为 L1,否则退到 L1 − 128。
一句话总结这段:这套设计把「上下文越长、重算越贵」变成了一项常数开销,代价是接受一个近似的 SWA 缓存。对长上下文场景,这笔买卖很划算。
调度:EPLB 参数与三层负载均衡
专家并行负载均衡(EPLB) 的机制是:每张卡除自己负责的专家外,还持有若干冗余专家(承载热点专家的副本);统计最近 N 步各专家负载,每 M 步重新分配一次,专家权重在组内通过 NVLink symm_mem 洗牌。两阶段给的默认值不同,这是可以直接抄的参数:
- Prefill:起点 每卡 4 个冗余专家(向上取整让专家总数是
ep_size的倍数),统计窗口 100 步,每 1,000 步重分配一次;实测moe_lb(最慢 rank / 均值)= 1.074(EP4DP4)/ 1.045(EP2DP2)。 - Decode:起点 每卡 1 个冗余专家(同样向上取整)——EP64 时取整后是每卡 7 个专家(不含共享专家);统计窗口 1,000 步,每 10,000 步重分配一次;实测
moe_lb= 1.090。
注意 decode 的窗口和重分配周期比 prefill 大一个数量级,因为 decode 的负载分布更稳定,频繁重排反而得不偿失。
数据并行侧有三层调度。 Prefill Load Balancer 的分桶逻辑很具体:按总上下文长度分 4 档——<16K / 16–64K / 64–256K / ≥256K;每档分到多少引擎,按该档上一轮占的计算配额需求动态重分配,每档有自己的单批计算配额上限。跨 DP 组时引擎按剩余计算配额拉活,因为任何引擎都能命中任何前缀,所以不做前缀亲和路由。单卡内请求 FIFO 出队,而 chunk 大小不是固定的——每个 prefill 步被授予一个时间预算(compute_quota_limit,单位纳秒),用一条拟合出来的二次成本模型 cost(q, c)(q = 序列 token 数,c = 该序列已算的前缀长度)去解最大的 q,在不超过 32,768 token 预算的前提下刚好用掉剩余时间预算;这个预算还会被 DP 组内的 all-reduce MIN 进一步压低。用二次成本模型反解 chunk 大小这个做法,在按时间片调度的地方很值得借鉴。
Decode Load Balancer 的原则是同时平衡两个维度:请求数(决定每步 batch 大小)与KV 缓存总长度(决定 attention 部分的代价)。启发式做法是把短请求发给并发低但 KV 总长较大的卡,把长请求发给 KV 总长较小的卡。视觉编码器的负载均衡则按机器粒度路由:prefill 机器随机挑一台「每卡一个 VE」的机器,再在该机器上选当前处理图像 patch 数最少的卡,patch 数直接由尺寸算出:(width/patch_size) × (height/patch_size)。
这套负载均衡的整体效果,官方给了一个可对比的参照:能达到理想均衡离线 benchmark 的约 80%。
DSpark 变长验证:官方把收益表也给了
推测解码这块,DeepInfer 用的是 DSpark 的变长验证。逻辑讲得很清楚:草稿 token 的接受概率随请求和位置变化,固定长度验证会把验证算力浪费在低概率 token 上。DSpark 用一个 Confidence Head 预测每个草稿 token 的接受率,调度时把验证算力分配给边际收益最高的 token。线上草稿模型一次生成 5 个 token,然后按剖面出来的硬件吞吐曲线与逐请求逐 token 的接受率,对草稿 token 排序,选出最大化有效吞吐的前缀子集去验证。
收益官方直接列表(EP64/DP64 benchmark 数据,生产工作点约每 rank 60 并发):
| 并发(seqs/rank) | 固定 5 平均验证长度 | 变长平均验证长度 | 固定 5 接受长度 | 变长接受长度 | 固定 5 吞吐(tok/s/rank) | 变长吞吐 | 变长相对固定 5 |
|---|---|---|---|---|---|---|---|
| 16 | 5 | 4.44 | 3.32 | 3.24 | 4,891 | 4,816 | −1.5% |
| 32 | 5 | 3.65 | 3.25 | 3.17 | 8,964 | 9,111 | +1.6% |
| 48 | 5 | 3.94 | 3.30 | 3.26 | 12,032 | 12,467 | +3.6% |
| 60 | 5 | 3.58 | 3.30 | 3.21 | 13,730 | 14,466 | +5.4% |
| 72 | 5 | 3.24 | 3.29 | 3.12 | 14,962 | 15,961 | +6.7% |
| 96 | 5 | 3.24 | 3.30 | 3.15 | 16,817 | 18,660 | +11.0% |
怎么读这张表:变长验证把每步验证的草稿 token 数砍掉约 30%(5.00 → 3.2–4.4),而平均接受长度基本没动(3.29 对 3.20);吞吐收益随并发单调上升,生产点(约 60 并发/rank)是 +4% 到 +7%,96 并发时到 +11.0%,但低并发(16)反而是 −1.5%。
这条负收益很值得记:变长验证在低并发下是赔的。如果你的部署跑在低并发区,直接固定 5 长度更省事。
有个有意思的耦合:变长验证要和异步调度配合。schedule(N+1) 执行时,第 N 步的 confidence 还没返回,所以 CPU 侧调度只固定总预算,逐请求长度改由 GPU 上的 TileLang kernel 重算(累乘、全局排序取 top-k、融合 remap scatter),结果写回一块 pinned buffer。
工程细节:异步调度、低精度与一致性
异步调度(zero-overhead batch scheduler) 的问题描述得很清楚:decode 第 N 步跑 schedule(N) → forward(N) → sample(N) → post-process(N),其中 schedule 与 post-process 是纯 CPU 逻辑,而 post-process(N) 之前需要一次 GPU-CPU 同步去取 sample(N) 的结果——于是 GPU 在 post-process(N) 和 schedule(N+1) 期间是空转的。解法是照 SGLang、NanoFlow 已有的做法在 GPU 与 CPU 之间异步重叠:GPU 跑 forward(N) → sample(N) 的同时,CPU 跑 schedule(N+1) → launch_forward(N+1) → post-process(N−1) → launch_sample(N+1),让 GPU 全程没有空窗。README 里注明默认开启。
低精度的三处用法,官方按「量化粒度 + 格式」写死了:主 KV 用 per-16 / e4m3——类似 NVFP4 但没有全局 scale;indexer KV 与 MoE 路由专家权重都用 per-32 / ue8m0,即 MXFP4。做量化对比实验时,这三处就是你要对齐的基线。
Kernel 层面有两个可直接引用的结论:Profile 用仓库里的 examples/benchmark.py 拿 trace;Mega MoE 在 scale-up 域内是通用解——不管 EP 大小与 batch 大小,都显著快于未融合的 MoE。至于内部版 kernel 库是否更强,官方说大体相同,只是 indexer 与 mega 系列有些差异,并主动交代 FlashMLA 少了一处手写 SASS 优化,导致 decode kernel 慢 5%–10%,端到端差距最多 5%。这种「自曝差距」的口径比「完全一致」可信。
一致性是这套框架少见的卖点,两个开关值得记住:DEEPINFER_BATCH_INVARIANT=1 让不同 batch 组成下模型结果完全一致(仅 NVIDIA);要确定性采样(相同输入 token 与采样参数给出相同输出),需要 DEEPINFER_BATCH_INVARIANT=1 DEEPINFER_ENABLE_MTP_VARLEN=0 并给 SamplingParams 传一个固定的 seq_seed(同样仅 NVIDIA)。做评测、回归测试、审计类业务,这两条比多几个点的吞吐更值钱。
结构化输出走的是编译与推理解耦:SO 编译跑在一台专用 SO 机器上异步做,把格式约束(JSON schema / 正则 / EBNF)编译成文法自动机;请求进引擎前先取编译结果;运行时每个 decode 步推进自动机、确定合法 token 集合、生成 mask 作用到采样逻辑上。
自动扩缩容与容错
- 扩容依据:prefill 按未命中缓存的 token 数决定需要多少机器,decode 按运行中的请求数调整;调整被限速,且有上下界。
- 缩容的代价处理:decode 引擎(CE)收到关闭通知后进入 1 分钟宽限期,仍未完成的请求连同 KV 缓存一次性转给其它 CE 继续解码。
- 冗余配额:以两种形式保留——池子里留空转的就绪机器作为扩容库存,以及始终让在线实例多于计算出的需求;具体规则是配额利用率高于 0.8 时禁止缩容。
- 重启时间在分钟级;容错不靠缩短单引擎重启时间,而靠 PE/CE 的配额冗余 + 请求级重路由。
- EP 故障转移:大 EP 部署里一个实例的所有 GPU 构成单一通信域,一张卡坏掉整个实例就不可用。重启时平台会从当前所有健康 GPU 重新确定 EP 尺寸,实例以实际拿到的卡数作为自己的 EP 尺寸——这个「动态降级 EP」的做法,比强行等卡凑齐更务实。
DeepSeek Recipe:协议层的那些坑
deepseek-recipe 是另一半交付物,做的是 API 协议与 token 序列之间的编解码。三点值得单独记:
一、多协议收敛。 它把 Chat Completions、Responses、Messages 三种 API 的请求转成统一的对话格式,共用一套模型模板与编码逻辑;输出侧把模型输出解析成事件(正文、推理内容、工具调用),再转成各协议的流式响应。请求转换、提示词编码、输出解析都用 Rust 实现,配 Python 绑定。官方强调其处理逻辑与 DeepSeek 官方 API 完全对齐,第三方部署能拿到与官方一致的 API 行为。
二、防特殊 token 注入。 构建 prompt 时,显式插入的特殊 token 及其位置被记录下来,编码时直接映射到对应 token ID;普通文本走基础词表。而用户输入、工具结果、工具调用参数里出现的 <|User|>、<|image|> 这类字符串,会被编码成对应的普通 token 序列——文本无法注入控制 token 去影响模型行为。输出侧同理:靠 token ID 识别推理结束标记 “ 与 DSML 标签里的 |DSML|,模型用普通 token 拼出的同样文字会被正确保留为字面内容,不会获得触发推理结束或工具调用的能力。「按 token ID 而不是按文本匹配」这条,是自建网关必须照做的。
三、分段并行编码。 既然特殊 token 的位置和边界已知,按 BPE 的性质,特殊 token 之间的普通文本段可以独立编码,于是跳过在普通文本内部匹配切分特殊 token,并行编码各段再按原序拼接,降低超长上下文请求的编码耗时。
流式解析的细节也很实在:后端按 token 粒度返回 chunk,单个 UTF-8 字符、一个 DSML 标签或一个工具参数都可能被切在几个 chunk 之间。做法是遇到不完整 UTF-8 字符先缓冲到后续 token 补齐再交给解析器;解析器跨推理/正文/工具参数三个阶段保状态,用增量 KMP 匹配分隔标记,chunk 在标记中间断掉时保留匹配进度,下个 chunk 到了继续;已确定的内容随解析推进即时输出,不重复扫描前文。工具参数的转换则做到「工具名一解析出来就返回」,之后直接从 DSML 参数边界生成 JSON 片段,字符串值逐段 JSON 转义,数组/对象按原始 JSON 文本输出,不需要构造完整参数对象,也不重复解析与序列化。
上手:三条命令之后,账可以自己算
依赖:Linux、GCC ≥ 13.0、Python 3.12、CUDA 13(NVIDIA)或 CANN 9.2(昇腾)。构建前先准备一个干净的 Python 3.12 环境。
# 昇腾平台把 nvidia 换成 ascend,这个变量要在运行 DeepInfer 的同一环境里保持导出
export DEEPINFER_ACCELERATOR=nvidia
./build.sh
# 离线推理:默认 checkpoint 是 deepseek-ai/DeepSeek-V4.1-Flash-1017,
# 首次运行会下到 HF 缓存;想用本地副本就设 DEEPINFER_HF_MODEL_DIR
export DEEPINFER_HF_MODEL_DIR=/path/to/DeepSeek-V4.1-Flash-1017
python examples/offline_inference.py
想打开几个关键特性,README 给的环境变量是这几个:
# DSpark 变长验证(DEEPINFER_USE_NEXTN_DECODE 控制最大草稿长度,默认 5)
export DEEPINFER_ENABLE_MTP_VARLEN=1
# Decoder SWA bounded replay:V4.1 的默认设定就是这两个值
# 保留的后缀必须覆盖 SWA 窗口 + 最大回溯长度
export DEEPINFER_YOCO_PREFILL_TRUNCATION_MODE=compact
export DEEPINFER_YOCO_PREFILL_KEEP_TOKENS=140
# batch-invariant 推理(仅 NVIDIA);叠上 varlen=0 + 固定 seq_seed 即可确定性采样
export DEEPINFER_BATCH_INVARIANT=1
结构化输出是把 so_args 传给 SamplingParams(字段形态以仓库里 examples/_structured_output.py 为准):
from deepinfer.sampling_params import SamplingParams # 以仓库实际导入路径为准
sp = SamplingParams(
# 约束生成严格符合给定 schema / 正则 / EBNF
so_args={"schema_type": "json_schema", "schema": my_schema},
)
# 要确定性输出时(仅 NVIDIA):
sp_det = SamplingParams(
seq_seed=12345,
)
关于 benchmark 的口径:官方那两张帕累托图是在 4 张 SM100 上测 prefill、64 张上测 decode,各自都在一个 scale-up 域内;复现脚本是 examples/sweep/run_sweep.sh。拿它和你的实测对比时,先确认这两条前提,否则数字对不上很正常。
下面这段脚本不需要这台机器有 GPU,纯粹把官方公开数字做成可核的账——这类「拿官方数字反推容量」的活,比转述容量结论有用得多:
# capacity_math.py —— 只用官方公开的常量反推部署容量,不依赖 GPU
SEQ_BYTES = 1_415_040 # SWA state: 20 层 × 134 token × 528 B
STORAGE_PER_GPU_TB = 14.4 # 官方:每 GPU 约 14.4 TB(逻辑口径)
def swa_state_units(n_sequences: int) -> tuple[float, float]:
"""SWA 状态 1 个序列约 1.35 MiB,且不上持久化存储、只留本地内存。"""
total = n_sequences * SEQ_BYTES
return total / 1024**3, total / 1024**2
def storage_servers_for(gpus: int, ssd_tb: float = 30.73, n_ssd: int = 20) -> float:
"""一台存储服务器 = 20 × 30.73 TB 原始 SSD。"""
per_server = ssd_tb * n_ssd # 614.6 TB 原始
need_raw = gpus * STORAGE_PER_GPU_TB # 逻辑需求
# 3FS 用 10+2 纠删码:写 12 份数据存 10 份有效 → 原始至少放大约 1.2 倍
return need_raw * 1.2 / per_server
if __name__ == "__main__":
import json
gb, mib = swa_state_units(1) # 单序列
print(f"单序列 SWA state:{mib:.2f} MiB")
print(f"100 万序列 SWA state:{swa_state_units(1_000_000)[0]:.1f} GiB")
# 官方最小高效单元:64 卡 prefill + 64 卡 decode
n_gpus = 64 + 64
print(json.dumps({
"gpus": n_gpus,
"logical_kv_tb": round(n_gpus * STORAGE_PER_GPU_TB, 1),
"raw_kv_tb_with_ec": round(n_gpus * STORAGE_PER_GPU_TB * 1.2, 1),
"storage_servers_needed": round(storage_servers_for(n_gpus), 2),
}, ensure_ascii=False, indent=2))
把 128 卡的官方最小单元代进去,逻辑 KV 需求是 1,843.2 TB,按 10+2 纠删码放大后约 2,211.8 TB 原始容量,至少要 4 台(20×30.73 TB)存储服务器;如果再叠加「留存 24 小时」的实际写入量,这个数只会往上走。文件存储账算到这一步,采购讨论才有靶子。
坑位清单
- 仓库不是服务全家桶:KV cache 系统、跨 DP 负载均衡两块没开源,README 与部署问答都写明了,别到部署阶段才发现。
- 只支持 V4.1-Flash,只支持 SM100 与昇腾 950:其它卡要自己适配 kernel,不是改个配置的事。
- P
大约 1:1,decode 建议 EP64 起,所以最小高效单元是 64+64 卡。小规模试验想你自己的比例之前,先接受这条基线。 - 低并发别开变长验证:并发 16 时是 −1.5%,只有爬到 40+ 才转正。
DEEPINFER_ACCELERATOR要在运行环境里持续导出,只 export 一次给 build.sh 是不够的。- batch-invariant 与确定性采样都标注仅 NVIDIA;叠确定性采样还要关掉
DEEPINFER_ENABLE_MTP_VARLEN并固定seq_seed。 - 缩容水位是 0.8:配额利用率高于它就禁止缩容,做容量评估时别按 100% 计算。
- 跨机房部署要按每 8 卡约 400 Gbps 预留互联,且官方提醒跨机房光纤的物理风险。
- TTL 是取舍不是常数:chatbot 用 3 天 TTL 会丢 0.5%–1% 命中率,长尾重的业务要给更长的 TTL。
- 别信「每百万 token 成本」类转述:这份官方材料只给了 155 万亿 token/天与每 GPU 小时 38 美元,没给卡数。
总结
DeepInfer 这次开源的价值,不在于「又一个推理框架」,而在于它把一套为超大规模线上服务调过的工程取舍摊开了:用商品化 8 卡机做 prefill、64–128 卡超节点做 decode;把 block KV 放到独立的 3FS 集群,换来「任意前缀任意机器可命中」;用 SWA 有界重放把长上下文的重算压成常数;用 Confidence Head 把验证算力按边际收益分配;再叠上 batch-invariant 与确定性采样这类「不为吞吐、只为可复现」的开关。
同时它也诚实地标出了缺口:缓存系统与跨 DP 负载均衡这两块——恰好是决定成本的两块——留在内部。想照着它改自己的框架,最好的路径是把它当一份「参考答案 + 设计说明」来读:知乎长文讲架构,部署问答讲参数,仓库给实现。下一步 DeepSeek 说会继续在这里开源新模型的推理代码,这份仓库值得持续盯着。
需要提醒的是:本文所有数字都来自 DeepSeek 官方 README、doc/inference_deployment_qa.md 与官方博客口径,其中 benchmark 类数字是厂商自测,未在第三方硬件上复现;部署问答也明确说明它会持续更新,接入前请以仓库当前版本文档为准。
参考与致谢
- DeepInfer 仓库与 README:https://github.com/deepseek-ai/DeepInfer
- DeepInfer 部署问答(本文参数主要来源):
doc/inference_deployment_qa.md - 协议编解码组件:https://github.com/deepseek-ai/deepseek-recipe
- 默认 checkpoint:https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash-1017
- DeepSeek 关于 V4.1 Flash 推理系统的知乎长文(缓存与负载均衡两块的设计说明)
声明:本文为技术解读,不构成任何投资建议。文中「厂商自测」类数字均标注来源,未标注官方确认的推断均已显式说明。
DeepSeek 开源 DeepInfer:155 万亿 token/天的推理框架,和它没开出来的部分
- 作者
- 摆渡
- 发布于
- 许可协议
- CC BY-NC-SA 4.0
除另有声明外,本文采用上述许可协议。转载或改编请注明作者与原文地址,并遵守许可条款。