DeepSeek 把跑线上服务的推理框架开源了,叫 DeepInfer。
官方给的说法是:单集群每天 155 万亿 token,谷电价下每张卡每小时收入 38 美元。我看到这组数字的第一反应是去翻代码,看看能不能对得上。
翻完之后感受有点分裂。一边是调度器和投机解码,确实写得好,值得逐行读;另一边是那张漂亮的架构图,上面有三个大块在这个仓库里根本不存在。
这篇按我读的顺序写。能对上源码的地方标了行号,对不上的地方也说了。
它只跑一个模型
DeepInfer 不是 vLLM 那种通用服务框架。模型注册表里只有一条 DeepseekV41ForCausalLM,默认 checkpoint 是 DeepSeek-V4.1-Flash-1017。
| DeepInfer | vLLM / SGLang | |
|---|---|---|
| 支持模型 | 一个 | 几百个 |
| 入口形态 | 离线 LLM.generate() | OpenAI 兼容 HTTP API |
| 硬件 | NVIDIA SM100 / 华为昇腾 950 | 广泛的 GPU 型号 |
| 优化方式 | 和 V4.1 的结构绑死 | 通用抽象加插件 |
README 自己写了,最初是基于 vLLM 构建的,也参考了 SGLang 和 TensorRT-LLM。这话没错,但只说了一半。
Python 那层确实还留着 vLLM 的形状:LLM、EngineArgs、Sequence、SamplingParams,连注释都像。可 csrc/engine/ 底下的调度器、块管理、输入组装是重写的 C++,model_executor/models/v4_modules/ 里的模型结构也是为 CED、YOCO、Engram、DFlash 单独写的。
拿 vLLM 的壳,装了台为自家模型定制的发动机。这个比喻我觉得还挺准。
那张架构图,画的不是这个仓库
先看绿色那块,DeepSeek Recipe。它是协议层,把 Chat Completions、Responses、Messages 三种 API 的 JSON 统一成对话和 token 序列,再往引擎里送;引擎吐出来的 token 流也由它解析回各家协议的 SSE 响应。组件在另一个仓库 deepseek-recipe,不在 DeepInfer 里。
红色是 Entrypoint,请求入口。它会拿请求去查 KV cache 的前缀,只查元数据,走哈希链,然后决定这个请求该进 prefill 还是 decode。
黄框和橙框分别是 Prefill Service 和 Decode Service。前者吃没缓存的 prompt 算出 KV,后者一个 token 一个 token 地往外吐。每台机器内部还塞了一个 Expert-Parallel Load Balancer,管专家怎么分。
紫框是视觉编码器。图片先过 ViT 出 embedding 再进 prefill,多模态走 EPD 三阶段分离。
三个蓝色圆角框是三个负载均衡器,路由维度各不相同。VE 那个按图片 patch 数挑最闲的 GPU;Prefill 那个按上下文长度分四档(<16K、16–64K、64–256K、≥256K),按上一轮的算力配额动态分配;Decode 那个同时看请求数和总 KV 长度,短请求往并发低但 KV 长的卡上送。
最底下灰色的是 3FS 上的 KV 存储集群。粗蓝箭头是 cache 的读写,prefill 写完落盘,decode 按需拉。
整张图逻辑很顺。问题在于,红框的路由、三个蓝框、底下那层存储,这个仓库里都没有。
LLMEngine 里能翻到 init_pd_split()、do_save_kvcache()、send_processed_prompts() 这几个方法,点进去看,要么是 pass,要么是个默认实现。这是给内部系统留的扩展点,不是开源功能。
仓库分五层
具体到目录:
| 目录 | 干什么 | 规模 |
|---|---|---|
deepinfer/engine/ | 引擎主循环、参数解析、统计日志 | 6 个 py |
deepinfer/worker/ | Worker、ModelRunner、CUDA Graph、CacheEngine | 10 个 py |
deepinfer/model_executor/ | 模型结构、算子、并行通信、EPLB | 24 + 33 + 30 + 30 个 py |
deepinfer/ve/ | 多模态预处理 | 9 个 py |
deepinfer/structure_output/ | 结构化输出的编译与匹配 | 5 个 py |
csrc/engine/ | 调度器、块管理、输入准备、采样准备 | 约 20 个 cpp/hpp |
csrc/graph_timer/ | CUDA Graph / 昇腾 profiler 计时 | 4 个文件 |
deep_cluster/ | 硬件探测、NUMA 绑定 | 5 个 py |
vendor/ | 6 个 submodule:FlashMLA、DeepGEMM、DeepEP、DeepSelect 及昇腾版 | — |
热路径基本都在 C++ 里。原因也简单:调度器每生成一个 token 就要跑一遍,decode 阶段每秒几十到上百 step,Python 那点开销会被放大到不能忍。scheduler.hpp 一千三百多行,全是每个 step 的必经之路。
一个请求怎么走完
入口在 deepinfer/entrypoints/llm.py 的 LLM.generate()。编码 prompt、逐条 add_request、循环 step(),就这三件事。
主循环是 llm_engine.py:860 的 step(),一次调用产出一批 token:
# llm_engine.py:881
with cpu_timer.record("schedule"):
seq_metadata_list, scheduler_outputs = self.scheduler.schedule(
self.sequence_metadata_pool
)
# ... 处理 clone_states / so 状态 / 多模态 fetch ...
request_outputs = self._execute_model_async_step(seq_metadata_list, scheduler_outputs)
调度那步在 csrc/engine/scheduler.hpp:275。它要回答的是:这一步跑 prefill 还是 decode,跑哪些序列,每个序列跑多长的 chunk。
执行在 deepinfer/worker/worker.py:89。把调度结果变成 GPU 张量,forward,采样,异步模式下再把 post-process 推到下一轮。
源码里几个我想记下来的地方
调度器要投票
EngineType 有三种:PROMPT 只做 prefill,COMPLETION 只做 decode,NORMAL 两个都做。离线推理走的是最后一种,也对得上架构图里的 Prefill Service 和 Decode Service。
NORMAL 有个很实际的问题。一个 DP 组里所有 rank 必须做同一件事,rank 0 在 prefill、rank 1 在 decode,allreduce 就死锁了。
源码里的解法是投票,scheduler.hpp:295:
bool is_any_point = (decode_step != 0) && (decode_step % prefill_any_attempt_interval == 0);
bool is_all_point = (decode_step % prefill_attempt_interval == 0);
if (!is_any_point && !is_all_point) {
return _schedule_decode(acquire);
}
bool want = (get_gpu_cache_usage() <= GPU_CACHE_PREFILL_WATERMARK) && _has_prefill_waiting();
bool do_prefill;
if (is_any_point) {
// ANY:任一 rank 想做即触发
do_prefill = (_dp_allreduce(want ? 0 : 1, c10d::ReduceOp::MIN) == 0);
} else {
// ALL:全员想做才触发
do_prefill = (_dp_allreduce(want ? 1 : 0, c10d::ReduceOp::MIN) == 1);
}
ALL 点每 prefill_attempt_interval 个 decode step 出现一次,要求全员都有 prefill 活,避免一个 rank 干活、其他 rank 陪跑。ANY 点间隔更短,任一 rank 有活就一起做,给孤立的那个 rank 兜底。
后面还有第二轮投票。want 只是看 waiting 队列非空,_schedule_prefill 仍可能因为 slot 满了、KV 块不够而空手而归。全组都空手就得转 decode,不然后面会陷入"做空 prompt step 把 decode_step 清零,下一步接着投票接着空手"的循环。
另外 GPU_CACHE_PREFILL_WATERMARK 是 0.9,缓存占用过了九成就不再接 prefill。
这段投票逻辑我没在别的解析文章里见过,但它是"分离部署"这套架构落到单机离线场景时最真实的妥协。
显存是切成块管的
csrc/engine/block_manager.hpp 的思路和操作系统管内存页差不多。显存切固定大小的 block,按需分配,引用计数,用完回收。
核心就三个方法:
int allocate(); // 从 free_blocks 链表取一个
void free(int block_id); // 还回去
AllocStatus can_allocate_nolock(int to_allocate_blocks);
这么做的收益很直接。请求长短不一,不用预留连续显存;相同前缀可以共享物理 block;请求被抢占时只放 block,不搬数据。
每个序列要多少 block,scheduler.hpp:191 是这么算的:
return (seq.get_len() + seq.generation.stopping.max_tokens + block_size - 1) / block_size + 1;
prompt 长度加上最大生成长度,向上取整,再多留一个。
异步调度省的不是计算,是等待
官方叫它 zero-overhead batch scheduler。这个说法我看完代码后觉得得打个折。
原来的流水线是这样:
schedule(N) → forward(N) → sample(N) → [等 GPU 结果] → post-process(N) → schedule(N+1)
↑ GPU 在这里空转
异步之后,GPU 跑 forward(N) 和 sample(N) 的时候,CPU 同时在跑 schedule(N+1)、启动下一轮 forward、处理上一轮的 post-process。等待那段被填掉了。
但 CPU 的开销没消失。事件同步、元数据处理、资源回收都还在,只是从"GPU 等 CPU"变成了"CPU 躲在 GPU 执行时间里"。
第三方那篇解析说得更直白:零开销是设计目标,不是事实。我同意。
对应开关是 DEEPINFER_ZERO_OVERHEAD_SCHEDULING。
DSpark 卖的是验证算力
普通投机解码很粗暴:草稿模型一次吐 5 个,大模型并行验,接受几个算几个。
但每个草稿 token 被接受的概率差很多。固定验 5 个,等于把算力平均撒在一堆低概率 token 上。
DSpark 加了个 Confidence Head,预测每个草稿位置的接受率,再决定验几个。入口在 csrc/engine/mtp_varlen.hpp:150:
void schedule(const std::vector<std::shared_ptr<Sequence>> &scheduled,
std::vector<int> &seqlens_q) const {
...
// 维护两个 queue,一个用于计算 best_bsz,一个用于计算 seqlen_q
std::vector<std::pair<float, int>> acc_queue_for_bsz;
std::vector<std::pair<float, int>> acc_queue_for_seqs_q;
把每个草稿位置按置信度排队,枚举验证规模,挑单位时间产出最大的那个。
产出怎么估?两个运行时估计器。ForwardRuntimeEstimator(mtp_varlen.hpp:48)拿最近观测到的 forward 耗时去修正启动时 profile 的那条曲线;SamplerRuntimeEstimator(mtp_varlen.hpp:20)用滑动窗口中位数,优先取同一请求数下的样本,样本不够再回退到全局。
这里有个特别容易写错的地方,注释里写得很清楚:
// 需要保证 confidence_{i+1, ..., nextn} 不影响 draft_token_i 是否被验证。
// 假设此时在第 N 轮,我们期望使用 N-2 轮的 next_n_token_accept_rate 去计算 best_bsz;
// 并使用 N-1 轮的 next_n_token_accept_rate 去分配每个请求的 seqlen_q
两个约束叠在一起。空间上,后面位置的置信度不能反过来影响前面位置要不要验,不然逻辑就自相矛盾。时间上,异步流水线里第 N 轮调度时第 N-1 轮的结果还没回来,所以 batch 规模用 N-2 轮的接受率估,每个请求的验证长度用 N-1 轮的估。
为了不漏账,每个请求带 schedule_cnt 和 process_cnt 两个计数器,配一个断言:
TORCH_CHECK(mtp.process_cnt <= mtp.schedule_cnt && mtp.schedule_cnt <= mtp.process_cnt + 1, ...);
差值为 0 说明前后配对,用 N-1 轮的数据;差值为 1 说明最新一次 forward 还没产生接受率,得再往回退一轮。
官方数据是:验证长度从固定 5 降到 3.2 到 4.4,平均接受长度基本没变(3.29 对 3.20),生产工作点大概 60 并发每 rank,吞吐提了 4% 到 7%。
打开开关 DEEPINFER_ENABLE_MTP_VARLEN=1,最大草稿长度由 DEEPINFER_USE_NEXTN_DECODE 控制,默认 5。
EPLB 盯的是最忙的那张卡
MoE 的毛病是专家热度不均。有的专家天天被路由到,有的基本闲着,最后所有卡一起等最慢的那张。
EPLB 的思路是给热门专家加冗余副本。eplb/policy.py 分两步走:先按最近 N 步的 token 负载决定复制谁,再用贪心把 physical 副本摆到各 rank 上。
第二步的目标函数有个细节我觉得挺关键:它盯的是历史最忙的 rank,不是长期平均负载。看平均会漏掉尾延迟,而卡住整批的就是尾延迟。
冗余副本数还要求能被 ep_size 整除:
def resolve_num_extra_experts(num_routed_experts, min_num_extra_experts, ep_size):
"""取不小于下限、且使 num_physical_experts 能被 ep_size 整除的冗余 expert 数。"""
padding = -(num_routed_experts + min_num_extra_experts) % ep_size
return min_num_extra_experts + padding
重排的动作是 expert_model_parallel_shuffle_inplace,通过 NVLink 的 symmetric memory 直接搬权重,不走网络。
效果官方给的是 prefill moe_lb 1.074(EP4DP4)和 1.045(EP2DP2),decode 1.090。这个指标是最慢 rank 除以平均值,1.0 是完美。
YOCO 的 140 不是截断
V4.1 的层分两种:真正做 prefill 计算的 Encoder 层,和复用前面状态的 Consumer 层(YOCO,You-Only-Cache-Once)。
麻烦出在 KV cache 命中时。如果只命中了 block KV,SWA(滑动窗口注意力)的 KV 是缺的。朴素做法是整段重算,可 YOCO 下重建 decoder 状态要把最后 2560 个 token 跑过 20 层,太贵。
实际做法是只重算最后 128 个 token,并且把注意力窗口截在重放段里面。官方原话是:
replaying from position s, the query at position i is computed only against keys in the range max(s, i−127) to i
滑动窗口本来也只看最近 127 个位置,重算更早的没意义。这样重算成本变成常数,跟上下文长度和层数都无关。
README 里给的配置是:
DEEPINFER_YOCO_PREFILL_TRUNCATION_MODE=compact
DEEPINFER_YOCO_PREFILL_KEEP_TOKENS=140
这个 140 我看到过好几种误读,最常见的是"上下文被砍到 140 个 token"。完全不是。它是特定 decoder 路径的后缀重放长度,不截断用户输入,也不缩模型 cache。
约束是 keep_tokens >= window_size + max_backtracking_tokens。投机解码要回滚,回滚长度得留够窗口历史。默认 nextn=5 时回溯余量是 6,下限就是 134,示例里取 140 多留了几个 token 的余量。
一套逻辑,两套算子
支持昇腾不是"能跑起来"这么简单,两套 kernel 都做了优化。
deepinfer/model_executor/layers/tilelang_ops/
├── cuda/ 30 个 .py # CUDA 版 TileLang 算子
└── asc/ 30 个 .py # 昇腾版,一一对应
采样、融合采样、KV 读写、SWA 索引、DFlash、结构化输出、压缩、gather、norm,都能找到对应的两份。上层靠 PLATFORM 判断走哪套,模型结构、调度、并行通信共用。
底层 kernel 库也是双份 submodule:FlashMLA、DeepGEMM、DeepEP、DeepSelect,加上 DeepGEMM-Ascend 和 DeepEP-Ascend。
这块我觉得是这次开源对国内团队最实在的部分。算法逻辑一份,硬件适配两份,CUDA 和 Ascend 的差距能压到最小。
确定性和结构化输出
Batch-invariant 说的是同一份输入换个 batch 组合,结果得完全一样。这件事难在 GPU 上的 reduction、矩阵乘法分块、FP8 量化粒度都会随 batch 形状变,不同 batch size 下的 logits 可能数值上就有差。
DEEPINFER_BATCH_INVARIANT=1 打开,目前只支持 NVIDIA。tests/models/test_batch_invariant.py 的验证方法是先整批生成一条轨迹,再单独重放同一条轨迹,比对 token、logprob 和 top-logprobs。
注意它验证的是沿固定轨迹的数值一致性,不是任意随机跑都输出一样。
确定性采样更严格,要求相同输入 token 加相同采样参数出相同输出。需要三个条件一起:
DEEPINFER_BATCH_INVARIANT=1
DEEPINFER_ENABLE_MTP_VARLEN=0
# 并且给 SamplingParams 传固定的 seq_seed
关掉变长验证,是因为动态的验证长度本身就会改变执行路径。
结构化输出用的是 xgrammar,requirements.txt 里锁的 0.2.2,支持正则、JSON Schema 和 EBNF。它是编译和推理解耦的:schema 先编译成语法自动机,有专门的 SO builder 进程异步跑,DEEPINFER_SO_BUILDER_CPUS 控并发;推理时每个 decode step 推进自动机,算出合法 token 集合,生成 bitmask 打到采样上。
llm_engine.py:913 附近还有段守卫:最后一个 prefill chunk 的 SO 状态如果不对,直接把请求判死,免得整个调度循环卡住。
三张性能图,和四个容易踩的误读
官方给了三张图,都有价值,但不能当跨框架对比的结论用。
第一张是 prefill。配置写在图上方:EP4DP4 | 384+32 experts | context 131,072 | Decoder Bounded Replay (140 tokens)。横轴是一个 prefill step 的耗时,纵轴是非缓存输入吞吐(tokens/s/GPU),点上的数字是每个 DP rank 的 token 数。
从 4096 加到 32768,吞吐从 60000 涨到 87000 附近。再从 32768 加到 65536,吞吐只到 89000,延迟却从 380ms 涨到 730ms。
这就是 chunked prefill 的拐点。再往上加批量,等于拿延迟换几乎为零的吞吐。社区里默认的 max_num_batched_tokens=32768 和这个拐点是对得上的。
第二张是 decode。EP64DP64 | 384+64 experts | context 131,072 | nextn=3 fixed | per-token acceptance 75% → τ=3.25。横轴是单用户生成速度,纵轴是每卡吞吐,点上的数字是每个 DP rank 的并发请求数,不是总数。
换算是这么回事:
τ = 1 + nextn × acceptance_rate # 1 + 3 × 0.75 = 3.25
per_user_tokens_per_s = avg_SPS × τ
tokens_per_s_per_gpu = avg_SPS × requests_per_DP_rank × τ / CP
并发从 8 加到 256,每卡吞吐从 2500 涨到 26000,单用户速度从 325 掉到 105。很标准的吞吐延迟权衡。
第三张把 EP8DP8 和 EP64DP64 叠在一起。同样的单用户速度下,EP64 的每卡吞吐高出一大截。单用户 250 tokens/s 时,EP8 大概 6000 tokens/s/GPU,EP64 有 15000。
原因不难想。Wide-EP 把专家摊到更多卡上,每步要读的权重总量不变,但每张卡读的部分少了。MoE 这部分本来就是 memory-bound,少读就快。再叠上 EPLB 把负载压平,TPS 就上去了。代价是更多的卡、更大的通信域、更难的故障恢复。
四个误读,我觉得值得单独列一下。
路由是受控的。DEEPINFER_FORCE_LOAD_BALANCE=1 配 Gate 的 force_random,测的是干净的性能上界,不是真实流量的热点分布。
测量窗是挑过的。跳过 warmup,跟 MoE 统计 flush 冲突的区间直接丢,正式 sweep 关 profiler。这些做法本身合理,但意味着数字是最佳稳态,不是平均体验。
请求是构造出来的。共享前缀 fork 加未缓存后缀,prefill 和 decode 分开测,不是冷缓存下的随机 HTTP 流量。
没有对照组。官方没给同配置的 vLLM 或 SGLang 数据,所以这三张图证明不了 DeepInfer 比谁快,只能说明它自己的扩展性。
decode 那张图还有一层:吞吐是按 75% 接受率换算的,脚本里明确拒绝 MTP_VARLEN=1 的数据,怕重复计算。所以它也证明不了 DSpark 在真实流量下能赚多少。那个数据得看官方 Q&A 里 60 并发下的表格,+5.4%。
别人怎么看
最完整的一篇源码分析是 aik8s.run 的《DeepInfer 架构与源码深度解析》,作者锁在首个 commit d585e65 纯读码,没跑 GPU,这点它自己交代了。里面几个判断我核对过,站得住:
EP64DP64 不是 4096 张卡,进程组复用 rank,CP=1 时 DP 可能直接被默认成 EP。零开销是目标。DSpark 的收益来自给验证工作定价。YOCO 的 140 是特定路径的后缀重放。EPLB 第二阶段盯最忙 rank。官方图表的四个前提。质量数据是上游参考值。硬件范围比宣传窄,H20、L20、RTX PRO 5000 Blackwell 都不在官方配置里,后者是 compute capability 12.0,和 SM100 的 10.0 不是一回事。构建会污染已有的 vLLM 环境,强制装 torch==2.13.0+cu132 和 NCCL 2.32.3,在同一个环境里做对比实验等于白做。
关于"缺 KV cache",它也讲清楚了:cache_engine.py 管的是引擎内部的 KV,压缩注意力、索引 cache、SWA 环形状态、跨层复用,这些都在;缺的是跨实例那一层的命名、定位、传输、共享和生命周期。
Hacker News 那条主贴有 2310 分,885 条评论,但讨论重心基本不在技术上,在钱上。有人算推理是赚的,有人提醒推理赚钱不等于 AI 生意赚钱,训练和研究还在烧,模型还会过期。也有人担心开源权重加一堆推理服务商会把价格打穿。
有个实操反馈挺有意思:V4.1 Flash 单看大概 300 tokens/s,但做 agentic 任务只快 25% 到 50%,因为思考轮次和跑命令都变多了。
中文媒体基本是通稿转述。多出来的一个数字是外部估算的毛利率 82% 到 96.94%,取决于缓存命中率和 GPU 租价。推算,不是官方说法。
开源的边界
这节可能是全文最实用的。仓库能跑的,和架构图画着的,差得比想象中多。
| 开源了 | 没有 |
|---|---|
离线 LLM.generate() 批量推理 | OpenAI 兼容的 HTTP 服务 |
| C++ 调度器 + 分页 KV + 抢占 | 跨实例 KV cache 存储(3FS) |
| 单机 / 多机 EP、CP、DP | EPD 三阶段分离部署 |
| EPLB 专家负载均衡 | Prefill / Decode / VE 三个负载均衡器 |
| DSpark 投机解码 + 置信度调度 | 自动扩缩容、故障转移 |
| SWA bounded replay | 前缀路由器 |
| 多模态 VE + ViT | 多副本间的请求路由 |
| 结构化输出 | 运维体系:指标、告警、限流、鉴权 |
| 双后端算子 | — |
还有一点容易被忽略。官方 Q&A 里那套 cost(q, c) 二次代价模型,用算力预算反解 chunk 大小,在仓库里搜不到。我 grep 过 compute_quota、cost_model,都没有。它属于没开源的 Prefill Load Balancer。
仓库里真正约束分块的是这几个东西:max_num_batched_tokens、prefill_max_sum_seqlen_k、block manager 的可用块数,还有那个 0.9 的水位线。别在 csrc/ 里找代价模型,它不在。
我会怎么用它
如果你的场景是 SM100 或者 Ascend 950 上跑 V4.1-Flash,而且做的是离线批量推理,比如数据清洗、合成、评测、批量改写,这东西可以直接用。
如果你想研究生产级推理引擎长什么样,想抄它的调度和解码设计,更值得读。它把"验证工作定价"这件事摊开写了,这个思路能搬到任何投机解码实现里。
但如果你要一个 OpenAI 兼容的服务去接前端、接 Agent,不行。要跑 Llama 或 Qwen,不行。在 H100、H20 或者消费卡上,不行。要多租户、限流、鉴权、计费,都不行。
想补齐的话,至少得自己写 HTTP 服务层,处理协议、流式、取消、超时和背压;得做请求路由,按输入长度、decode 在途负载和缓存位置来分;得补跨实例 KV cache 的命名、传输和生命周期;多副本之间还得有负载均衡;最后是 TTFT、TPOT、排队时间、专家负载这些指标。
工程上要做的事不少。
上手
构建环境必须是 Linux、Python 3.12、GCC 13 以上。
export DEEPINFER_ACCELERATOR=nvidia # 或 ascend
./build.sh
NVIDIA 要 CUDA 13,昇腾要 CANN 9.2。build.sh 会装 Python 依赖、把 NCCL 强制重装成 2.32.3、编译六个 vendor 库和 deepinfer._C 扩展。
这句提醒再说一遍:用全新的 Python 3.12 环境,装进已有的 vLLM 环境会改 torch 和 NCCL 版本。
跑第一个例子:
python examples/offline_inference.py --ep 8
--ep 8 是示例值,不是最低要求。默认模型会自动下到 HF cache,也可以用 DEEPINFER_HF_MODEL_DIR 指本地目录。
读码的话,我建议这个顺序:先看 examples/offline_inference.py 里怎么调用,再看 entrypoints/llm.py,然后钻进 llm_engine.py:860 的 step()。把这条主循环抓住之后,再去 scheduler.hpp:275 看调度,去 worker.py:89 看执行。投机解码单独看 mtp_varlen.hpp:133,模型怎么搭起来看 v4_modules/builder.py。
别从第一行开始读。这仓库更像浏览器的渲染引擎,不是业务代码,C++ 那层是排版引擎,Python 是上层 API,算子库是合成器。
最后说个我自己的感受。DeepInfer 最有价值的地方不是"又一个推理框架",而是它把一套完整生产系统里的协同设计露出来了:验证算力怎么定价,专家负载怎么压平,cache 怎么在多个阶段之间流转。这些在论文里通常只有结论,这里能看到代码。
至于那个 155 万亿的数字,它背后还有 KV 存储、负载均衡和分离部署三块没公开。仓库给你的是发动机,不是整车。
参考:
- DeepInfer 官方仓库
- DeepInfer 架构与源码深度解析 — aik8s.run
- DeepSeek V4.1 Flash 推理系统(官方博客)
- Hacker News 讨论
- DeepSeek-V4.1-Flash-1017 模型卡
文中的架构图和三张性能曲线来自 DeepInfer 官方仓库 doc/images/,版权归 DeepSeek-AI。源码引用对应首个公开 commit d585e65。