DeepSeek V4 源码剖析

第19章 vLLM / SGLang / FlashMLA 上的 V4 适配

作者 杨艺韬 · 7,662 字 · 发布于 · 更新于

模型没被服务起来之前,都还不是产品。

V4 的权重与推理代码在 HuggingFace、kernel 库在 GitHub 都摆好了,但要让它跑在生产的 vLLM / SGLang / TensorRT-LLM 上,还有最后一里的工程。


19.1 引子:从 HF 仓库到生产服务的"五件事"

把 V4 部署到生产需要解决的 5 件事:

  1. 引擎适配:让推理引擎认识 DeepseekV4ForCausalLM 这个 architecture,加载权重并跑 forward
  2. KV cache 改造:V4 的 KV cache 形状(滑窗 + 压缩段)不是任何引擎默认支持的
  3. 稀疏 kernel 集成:FlashMLA 的稀疏路径必须接入引擎的 attention 路径
  4. MoE 通信优化:用专门的 EP 通信替换引擎现有的 MoE 通信——可以接 DeepEP(2025-02 为 V3/R1 开源的 all-to-all 库),也可以用 V4 技术报告第 3.1 节自己的方案:DeepGEMM 里把 dispatch、两次 GEMM 与 combine 融成一个 kernel 的 Mega MoE。vLLM 0.8.5 的 EP 没有 all_to_all,靠每层一次 all_reduce 汇总;pin 版参考实现的 MoE 也只用 all_reduce(inference/model.py:641-642)
  5. 调度器协调:prefill / decode 分阶段、与 KV cache 形状协调

每件都是非平凡工作。本章按引擎拆这五件事的具体落地。


19.2 部署 V4 的"5 件事"流程图

把 §19.1 描述的 5 件事画成依赖关系图:

flowchart TB
  Start([HF 仓库下载 V4 权重]) --> A1[1. 引擎适配:<br/>注册 DeepseekV4ForCausalLM]
  Start --> A2[2. KV cache 改造:<br/>滑窗 + 压缩段双段]
  A1 --> A3[3. 稀疏 kernel 集成:<br/>FlashMLA 稀疏路径]
  A2 --> A3
  A3 --> A4[4. MoE 通信优化:<br/>DeepEP / Mega MoE 替代 all_reduce]
  A4 --> A5[5. 调度器协调:<br/>prefill/decode 分阶段]
  A5 --> Run([生产可用])
  classDef todo fill:#7c2d12,stroke:#fb923c
  classDef done fill:#312e81,stroke:#a78bfa
  class A1,A2,A3,A4,A5 todo

5 件事是部分串行 + 部分并行——引擎适配 1 与 KV cache 改造 2 可以并行做,但稀疏 kernel 集成 3 必须等两者就绪;MoE 通信 4 与稀疏 kernel 解耦但都要先有引擎;调度器 5 是最后一步综合。


19.3 vLLM 适配 V4 要动哪些地方(本节全是推断)

先说清楚性质:本专栏参照的 vLLM 是 v0.8.5,早于 V4,没有任何 V4 支持:它的模型注册表只认到 DeepseekV2ForCausalLM / DeepseekV3ForCausalLM,而 V4 的 config.json 写的是 DeepseekV4ForCausalLM、model_type: deepseek_v4,拿 0.8.5 直接加载 V4 会在架构识别这一步就失败。0.8.5 里现成的 MoE 加速也接不上:它的 EP 没有 all_to_all、没有 DeepEP 集成;可选的 DeepGEMM MoE 路径(vllm/model_executor/layers/fused_moe/deep_gemm_moe.py)调用的 m_grouped_gemm_fp8_fp8_bf16_nt_contiguous 等旧接口名,在本专栏 pin 的 DeepGEMM 891d57b 里已经不存在(PR #112 改了名),更没有 V4 routed expert 要的 FP8×FP4。所以 V4 要在 vLLM 上跑,靠的是需要做的改造,不是现成支持。下面六条不是对任何已存在 PR 的描述,而是从 V4 源码结构 + V3 当年的适配方式推出来的"该在哪些地方动刀"的地图。真实 PR 落地后一切以仓库为准。

PR 1:架构注册

在 vllm/model_executor/models/registry.py 增加一行映射。参考 v0.8.5 里 V2 / V3 的写法:

"DeepseekV2ForCausalLM": ("deepseek_v2", "DeepseekV2ForCausalLM"),
"DeepseekV3ForCausalLM": ("deepseek_v2", "DeepseekV3ForCausalLM"),

注意 V3 并没有单独的 deepseek_v3.py——它复用了 deepseek_v2 模块,因为架构差异小到能靠 config 分支吃下。V4 大概率不能这么办:HC 让 hidden 多出一维、Compressor 是带状态的、Indexer 是 attention 之前的额外一步,这些都不是"加个 if"能覆盖的。所以新增一个独立模块的可能性更大——但具体怎么落地要看 PR。

PR 2:KV cache 形状扩展

vLLM 0.8.5 的 KV cache 形状由 attention backend 决定:FlashAttention backend 是 (2, num_blocks, block_size, num_kv_heads, head_size),V3 走的 MLA backend 是 (num_blocks, block_size, head_size)(vllm/attention/backends/flash_attn.py:69-77、vllm/attention/backends/mla/common.py:261-267)。V1 的 SlidingWindowManager(vllm/v1/core/specialized_manager.py)能管纯滑窗层,但管不了"滑窗 + 压缩段"拼在同一层里的形态。V4 需要新增一个 KV cache 类型支持"滑窗 + 压缩段"。可能的做法:

  • 新增 WindowedKVCache 类,包装两段(滑窗块 + 压缩块)
  • BlockManager 给两段独立的 block 分配策略
  • attention backend 在 query KV 时区分两段

PR 3:FlashMLA 集成

在 vllm/attention/backends/ 下新增一个走稀疏路径的 backend 模块(0.8.5 里已有的 vllm/attention/backends/flashmla.py 是稠密 MLA 的 backend,稀疏这套要另起一个;新文件叫什么本专栏不预设),调用 FlashMLA 的稀疏路径——prefill 走 flash_mla_sparse_fwd,decode 走带 indices 参数的 flash_mla_with_kvcache(两者都在 flash_mla/flash_mla_interface.py,前者底层是 sparse_prefill_fwd、后者是 sparse_decode_fwd)。这个 backend 与现有的 FlashAttention / PagedAttention backend 并存——根据模型 config 自动选择。

PR 4:DeepEP 集成

(另一条路是直接接 DeepGEMM 的 Mega MoE,即 V4 技术报告第 3.1 节的方案;这里按 DeepEP 写。)在 vllm/distributed/parallel_state.py 新增 DeepEP buffer 的初始化逻辑。在 MoE 层 forward 中,如果检测到 V4 + DeepEP 可用,走 DeepEP 的 dispatch / combine;否则回退到 0.8.5 现有的 EP 做法——每卡只算本地专家,最后一次 all_reduce 汇总(vllm/model_executor/layers/fused_moe/layer.py:883-886),0.8.5 里并没有 all_to_all 可退。

PR 5:Indexer / Compressor 接入

V4 的 attention 不只是简单的"q @ k^T"——compress_ratio=4 的 30 层必须先调用 Indexer 算 topk_idxs(compress_ratio=128 的层不建 Indexer,直接取全部压缩槽位,inference/model.py:466-471)。vLLM 的 attention backend 需要为 V4 实现"两步 attention":先 Indexer score,再 sparse_attn。

PR 6:调度器适配

vLLM 调度器需要识别 V4 的特殊性:

  • 分块(chunked)prefill 要自己补:pin 版参考实现只支持 start_pos=0 的整段 prefill 加逐 token decode,start_pos>0 时滑窗与 Compressor 都只按单 token 写缓存(inference/model.py:518-532、:373-376),要分块就得改写 Compressor 的状态续接
  • decode 时单 token 走 incremental Compressor 路径
  • 长上下文 prefill 需要更大的 GPU memory budget

再强调一遍:以上六条全是推断。它们唯一的依据是 V4 的源码结构与 V3 当年的适配方式,读者应以仓库里真实的 PR 为准。


19.4 SGLang 的 V4 集成路径

SGLang 是另一个流行的 LLM 推理引擎,与 vLLM 在设计哲学上有差异:

  • vLLM 强调"PagedAttention + 通用 batching"
  • SGLang 强调"灵活的 RadixAttention 前缀缓存 + 结构化输出"

V4 在 SGLang 上的集成路径与 vLLM 类似,但几个特殊点:

特殊 1:RadixAttention 与 V4 的滑窗 KV

SGLang 的 RadixAttention 是基于"前缀树共享 KV cache"的——多个请求共享相同前缀的 KV。V4 的两段 KV 在这件事上表现不同:压缩段按位置顺序追加、写完不再改(kv_cache[start_pos // ratio]),天然能按前缀共享;滑窗段却是 128 槽的环形缓冲(kv_cache[start_pos % win]),decode 一步就覆写一槽,Compressor 还带着没凑满一组的 kv_state / score_state——这两样都得在前缀分叉点另存快照才能共享。

SGLang 的适配可能让 RadixAttention 只作用于压缩段(ratio=4 与 ratio=128 两类层的压缩 KV),滑窗段与 Compressor 状态每个请求独立。这是个工程权衡——前缀共享的好处对 V4 比 V3 弱,但仍有价值。

特殊 2:结构化输出与 Think 模式

SGLang 擅长结构化输出(JSON / regex / 文法约束)。V4 的 Think Max 模式生成长 think 链,设想中可以借这类约束解码去限制 think 段的格式与长度(这是本专栏的推想,SGLang 是否提供现成开关以其文档为准)。

特殊 3:FlashMLA 的复用

预计 SGLang 会直接 fork FlashMLA 仓库或把它作为依赖(推测)——稀疏 MLA kernel 在两个引擎共享。


19.5 TensorRT-LLM:英伟达官方路径

TensorRT-LLM 是 NVIDIA 官方的 LLM 推理引擎。它比 vLLM / SGLang 更接近硬件——直接生成 CUDA / TensorRT engine。

V4 在 TensorRT-LLM 上的考虑:

Pros:

  • TensorRT-LLM 的 plugin 机制能把稀疏 MLA kernel 编译成 TensorRT engine,性能可能比 PyTorch 调用 FlashMLA 略高
  • 对 B200 的原生 FP4 支持先行——NVIDIA 自家最了解
  • 对生产部署的"低延迟 + 高吞吐"双优化更深入

Cons:

  • TensorRT-LLM 的开发周期长——V4 发布后可能要数月才有官方支持(预测)
  • V4 的 HC、Compressor 等"非标准 transformer 结构" 在 TensorRT-LLM 的标准 plugin 库里没有现成实现,需要自定义
  • 模型迭代慢——V4 GA 之后 TensorRT-LLM 适配可能再延迟

社区使用 TensorRT-LLM 部署 V4 的比例预计显著低于 vLLM / SGLang——除非有低延迟 + 大规模部署需求。


19.6 量化部署的 trade-off

V4 的"原生" FP4 + FP8 混合精度已经是高度量化的。生产部署常有人问"能不能再压一压"——先把算术摆清楚再说。

原生权重的大小不用估:64 个 safetensors 分片的字节数加起来是 864.7 GB(805 GiB),反推每参数约 0.54 字节(第 15 章 §15.1 算过)。以此为基准:

精度路径 每参数字节 权重规模 精度损失 评价
原生 FP4 expert + FP8 其余 + ue8m0 约 0.54(实测) 864.7 GB 已被 QAT 训过 唯一被官方训练过的配置
全部反量化到 BF16 2 3.2 TB 无 显存翻近四倍,只有做数值对照时才有意义
INT8 (PTQ) 1 + scale 约 1.6 TB 叠加在 QAT 之上 比原生大近一倍——没有任何理由这么做
INT4 (GPTQ / AWQ) 0.5 + scale 约 0.85 TB 叠加在 QAT 之上 与原生几乎一样大,白搭一层误差

这张表最重要的两行是 INT8 和 INT4:INT8 比原生大近一倍,INT4 与原生几乎一样大。原因很直白——V4 的绝大多数参数已经是 4 bit 了,往 8 bit "量化"是在往回走;往 INT4 换则是拿一层额外的 PTQ 误差去换几个百分点以内的显存(每组 scale 开销不同,省下的从零到几个百分点不等)。

结论很硬:V4 没有"进一步量化"的空间。它出厂时就是量化态,而且 routed expert 的 FP4 是在后训练阶段经 QAT 适应过来的量化态(技术报告第 3.4 节,第 14 章)。

对生产部署,唯一稳妥的路径就是用原生权重。真跑不动,换 V4 Flash(284B / 13B 激活),不要去二次量化 V4 Pro。


19.7 多机部署:典型拓扑

V4 Pro 的多机部署典型拓扑:

flowchart TB
  subgraph 单机典型["单节点 8 卡 H200/B200"]
    direction LR
    G0[GPU 0] -.NVLink.-> G1[GPU 1]
    G1 -.NVLink.-> G2[GPU 2]
    G2 -.NVLink.-> G3[GPU 3]
    G3 -.NVLink.-> G4[GPU 4]
    G4 -.NVLink.-> G5[GPU 5]
    G5 -.NVLink.-> G6[GPU 6]
    G6 -.NVLink.-> G7[GPU 7]
  end
  subgraph 双机典型["2 节点 16 卡 (IB 互联)"]
    Node0["Node 0<br/>8 卡 NVLink"] -.IB 400Gbps.-> Node1["Node 1<br/>8 卡 NVLink"]
  end
  subgraph 四机典型["4 节点 32 卡"]
    N0[Node 0] -.IB.-> N1[Node 1]
    N1 -.IB.-> N2[Node 2]
    N2 -.IB.-> N3[Node 3]
    N0 -.IB.-> N2
    N1 -.IB.-> N3
  end

每种拓扑的并行配置:

拓扑 TP EP 能不能装下 V4 Pro 适用场景
8 卡 NVLink 8 8 H100(640 GB)装不下 864.7 GB 的权重;H200(1128 GB)装得下:均摊每卡约 108 GB,按参考实现实例化约 117 GB,只剩二十来 GB 余量 V4 Flash 的甜区;V4 Pro 在 H200 上可跑但并发受限
16 卡 IB 8 16 可以:均摊每卡约 54 GB 权重,参考实现(world_size=16)实例化约 63 GB V4 Pro 的舒服起点
32 卡 IB 8 32 宽裕,可支撑大 batch 与 1M 上下文 大并发服务化

"能不能装下"这一列来自 §19.6 那张表:V4 Pro 的权重是实测的 864.7 GB。均摊值只是下限——pin 版参考实现里 wq_a、wkv、shared expert、Compressor 等不切分的权重每卡各存一份,lm_head、Compressor 权重还提升成 FP32,在 meta 设备上实例化 inference/model.py 的结果是 TP=8 每卡约 117 GB、world_size=16 每卡约 63 GB(第 15 章 §15.1)。再叠加 KV cache 与 prefill 的临时激活:按 max_seq_len=1048576 的部署设定,参考实现以 BF16 存 KV,1M context 下每序列约 9.6 GiB(含 Indexer 那份,第 15 章 §15.13,每个 TP rank 各存一份);若按技术报告的混合格式(RoPE 64 维 BF16、其余 FP8)存,主 KV 约 4.7 GB(第 3 章 §3.10)。

另外注意,表里 TP 与 EP 分开设是引擎层的配置。pin 版参考实现 TP 与 EP 共用同一个 world_size(attention 按它切、routed expert 按它均分),而且 o_groups=16 把 world_size 限制在 16 以内(第 15 章 §15.16)。16 卡用参考实现就是 TP=EP=16;32 卡那一行只能在引擎自己实现了"TP 与 EP 解耦"之后才成立。

吞吐数字本节不给——它取决于 batch size、context 长度、prefill / decode 比例、EP 规模,而且 V4 的引擎适配尚未落地,任何 token/s 预期都是编的。等 vLLM / SGLang 有官方 benchmark 再说。


19.8 部署中的常见问题

V4 部署到生产时容易遇到的几个问题(基于 V3 部署经验推测):

问题 1:CUDA / 编译器版本不满足,而且三个库要求各不相同

别记成一个统一版本号——各库 README 的口径是:

  • DeepGEMM:SM90 需 CUDA 12.3+(建议 12.9+),SM100 需 12.9+;编译器要支持 C++20;PyTorch 2.1+(Mega MoE 要 2.9+);还要 CUTLASS 4.0+
  • DeepEP:SM80 需 CUDA 11.0+,SM90 需 12.3+;PyTorch 2.1+;外加 NVSHMEM(不装就没有跨节点与低延迟路径)
  • V4 自带的 inference/kernel.py:需要 TileLang,以及 fast_hadamard_transform(rotate_activation 用)

取交集才是你的镜像基线。

问题 2:NVSHMEM / IB 网络配置错误

DeepEP 的 internode 与 low-latency 路径都建在 NVSHMEM 上。容易踩的坑不是 IB 本身,而是编译时没找到 NVSHMEM——README 写明不指定 NVSHMEM_DIR 会禁用所有 internode 与 low-latency 功能。实际行为是:setup.py 先读 NVSHMEM_DIR,没设就找 pip 装的 nvidia.nvshmem,两样都没有时只打印一行 Warning 就继续编译(DeepEP 仓库根目录 setup.py 第 19–31 行,随后加上 -DDISABLE_NVSHMEM,同文件 :47-49),装出来的包只剩节点内那条路;等到跨节点或 low-latency 模式创建 Buffer 时,才被断言 NVSHMEM is disabled during compilation 拦下(deepep-567632d/csrc/deep_ep.cpp:167-169)。IB 侧则关注 NVSHMEM_IB_SL(虚拟通道隔离)与路由策略(重载开自适应路由、轻载用静态路由)。

问题 3:长上下文 prefill OOM

两个吃显存的大头要分开算:KV cache 在 1M context(max_seq_len=1048576 的部署设定)下,按参考实现的 BF16 口径约 9.6 GiB / 序列 / 卡(第 15 章 §15.13,因为 num_key_value_heads=1,每个 TP rank 都存一份完整的),prefill 的中间激活则随一次喂进去的 token 数线性增长——注意 HC 让层间残差流是 4 路的,这部分激活是普通 transformer 的 4 倍(attention / FFN 内部仍在 1 路上算,第 10 章 §10.6)。所以长 prompt 需要分块 prefill,块开多大要留够激活空间;但 pin 版参考实现只支持整段 prefill(§19.3 PR 6),分块得靠引擎实现。

问题 4:把 shared expert 的精度搞错

这里有个容易想当然的误解:以为 shared expert 是 BF16。它不是——MoE.__init__ 里 self.shared_experts = Expert(args.dim, args.moe_inter_dim, swiglu_limit=...) 没传 dtype,走的是 default_dtype——模块顶部初值是 BF16,但 Transformer.__init__ 按 config 的 "dtype": "fp8" 把它改成了 FP8 e4m3(inference/model.py:19、:776)。区别只在于 routed expert 在 expert_dtype: fp4 时显式传了 torch.float4_e2m1fn_x2(inference/model.py:623-627)。

所以正确的说法是:shared expert 是 FP8、routed expert 是 FP4。任何把 shared expert 也压到 FP4、或者反过来"还原"成 BF16 的做法,都偏离了官方权重训练时的数值路径。

问题 5:MTP 关闭时性能下降

V4 的 MTP 在推理时可以做投机解码(第 11 章 §11.5)。关掉它,就放弃了这份加速——V3 报告实测约 1.8 倍 TPS,V4 技术报告没有给出对应数字。注意 pin 版参考实现并不做投机解码:generate.py 逐 token decode,Transformer.forward 也不调用 mtp,这条路要引擎自己接(第 11 章 §11.12)。引擎接好之后,生产部署应保持 MTP 启用。


19.9 部署清单:V4 上线检查表

把 V4 部署到生产前的 checklist:

基础设施:

  • CUDA 版本满足各库交集(DeepGEMM:SM90 ≥12.3 / SM100 ≥12.9;DeepEP:SM90 ≥12.3)
  • 编译器支持 C++20;PyTorch ≥2.1(Mega MoE 需 ≥2.9)
  • H100 / H800 / B200 GPU + NVLink 已配置(A100 不行——DeepGEMM 不支持 SM80)
  • 多机部署:NVSHMEM 已安装且能被 DeepEP 编译找到(设 NVSHMEM_DIR,或按 DeepEP third-party/README.md 用 pip 装 nvidia-nvshmem-cu12),IB / RoCE 网络已配置

模型 / 库:

  • 从 HF 拉取了完整 V4 权重(包含 LFS 大文件)
  • 显存预算已核对:权重 864.7 GB(参考实现 TP=8 每卡实例化约 117 GB)+ 每序列约 9.6 GiB KV(max_seq_len=1048576、参考实现 BF16 口径,每 TP rank 各一份)+ prefill 激活
  • FlashMLA 已安装(针对你的 GPU 架构)
  • DeepGEMM 已安装(注意它的 kernel 不在安装时编译,而是运行时 JIT——记得配 DG_JIT_CACHE_DIR 持久化)
  • DeepEP 已安装(多机部署;编译时要找到 NVSHMEM,否则跨节点与 low-latency 路径被禁用)
  • TileLang 与 fast_hadamard_transform 已安装(跑官方 inference/ 参考实现时需要)

引擎:

  • vLLM / SGLang 升级到包含 V4 适配的版本
  • config.json 已正确加载
  • 测试单机 prefill + decode 通过

性能:

  • 跑过 throughput benchmark,确认达到预期
  • 测试长 context(≥256K)prefill 不 OOM
  • 测试 batch=8/16/32 下的延迟分布

监控:

  • 接入 Prometheus / Grafana 监控
  • 设置 OOM、GPU temp、IB 错误的告警
  • 部署金丝雀流量并观察输出质量

完成所有 19 项,V4 才算"生产就绪"。


19.10 动手实验:用 vLLM 跑 V4

# 假设 vLLM 已经合并 V4 适配
pip install --upgrade vllm

# 单机 8 卡部署 V4 Pro(需 8 × H200 这一档,8 × H100 装不下,见 §19.15 Q1)
python -m vllm.entrypoints.openai.api_server \
    --model deepseek-ai/DeepSeek-V4-Pro \
    --tensor-parallel-size 8 \
    --max-model-len 1048576 \
    --dtype auto \
    --enable-prefix-caching \
    --gpu-memory-utilization 0.92

# 测试请求
curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
      "model": "deepseek-ai/DeepSeek-V4-Pro",
      "messages": [{"role": "user", "content": "解释 V4 的 Compressor 设计"}],
      "max_tokens": 1024
    }'

如果一切配置正确,会得到一个完整的 V4 回答。如果遇到 OOM 或 attention 错误,回到 §19.8 检查常见问题。


19.11 部署 V4 的可观测性体系

把 V4 部署到生产,必须建立完整的可观测性体系——它的复杂度远超 dense 模型。给一个完整指标清单:

GPU 层:

  • nvidia-smi: utilization / memory / temp / power / clock
  • nsys / ncu(Nsight Systems / Nsight Compute): kernel 时间分布、SM 占用率
  • HW counter: TensorCore TFlops、L2 hit rate

模型层:

  • attn_sink ratio(每层):softmax 里流向 attn_sink 的权重占比
  • expert load 分布(每层):384 expert 被使用频率
  • MTP acceptance rate:投机解码命中率
  • routing entropy:Gate 是否塌陷
  • HC comb 矩阵的双随机性:是否被破坏

引擎层(vLLM / SGLang):

  • TTFT (Time to First Token)
  • ITL (Inter-Token Latency)
  • Throughput (tokens/sec)
  • Queue depth:等待请求数
  • Prefill / decode 比例

业务层:

  • API 错误率 / 超时率
  • 输出 token 长度分布
  • 用户取消请求率
  • 重试率

告警阈值:下面这些数字没有一个来自官方——它们是"该监控什么"的示例,不是"该设多少"的答案。每一条都必须先在你自己的流量上跑一段时间、看清基线分布,再定阈值:

  • attn_sink ratio 显著高于基线:可能是稀疏 attention 大面积选错
  • expert load 的最大/最小比持续走高:路由失衡
  • MTP 接受率相对基线明显下滑(V3 报告的基线是 85%–90%):投机解码在退化
  • TTFT / ITL 的 P99 偏离基线
  • GPU throttle reason 出现(nvidia-smi -q -d PERFORMANCE)

这套体系的部署需要 Prometheus / Grafana 等基础设施 + 自定义 V4 专用 exporter。工程量本专栏估计在一两周量级(经验估计,无统计出处)——但它是生产可靠运行的前提。


19.12 V4 部署的延迟分布特征

V4 的延迟分布与 dense 模型有几个不同特征——理解这些特征对 SLA 设计极重要:

特征 1:长上下文 prefill 主导 TTFT,但增长比 dense 模型慢

dense 模型的 attention 是 O(S²),TTFT 随 prompt 长度超线性增长。V4 的稀疏 + 压缩几何把这条曲线压平了不少——README 的官方口径是"1M 上下文下 V4-Pro 相比 DeepSeek-V3.2 只需 27% 的单 token 推理 FLOPs 与 10% 的 KV cache"。

但别把它读成"TTFT 是常数":prefill 仍要过一遍全部 prompt token(linear 投影、Compressor 的池化、Indexer 的打分都是 O(S) 的),只是 attention 那一项不再是 O(S²)。具体的 TTFT-长度曲线要实测,README 没有给。

特征 2:MTP 让 ITL 呈双峰

num_nextn_predict_layers=1 意味着每步最多产出 2 个 token,于是 ITL 天然分成两支:

  • 峰 1:MTP 被接受,这一步吐出 2 个 token,摊到每 token 的时间减半
  • 峰 2:MTP 被拒,这一步只吐 1 个 token,还白付了一次 MTPBlock 的 forward

借用 V3 报告的 85%–90% 接受率(V4 技术报告没有给出),两个峰的权重大致在 85:15 到 90:10 之间。SLA 要按 P99 设——P99 落在慢路径上。

特征 3:MoE 通信导致 ITL tail 较长

V4 的 MoE 通信(DeepEP)虽然快,但仍有抖动——某些 step 会因为 dispatch 拥塞明显慢于平均(倍数取决于集群与负载,需实测)。这种 tail 在 P99 / P999 上明显。

特征 4:Hash 路由不会让某些 token 更快

有一种直觉是"前 3 层 Hash routing 直接查表,某些 token 会更快"。按 pin 源码看不成立:hash 层的 Gate.forward 照样算完整的打分,只是把 topk 换成 tid2eid[input_ids] 查表(inference/model.py:564-584),省下的只是一次 topk;而且 decode 是整批同步推进,单个 token 的路由差异不会体现为它自己的延迟。与 hash 路由真正相关的是负载:tid2eid 是固定映射,前 3 层的专家负载跟着线上流量的 token 分布走(本专栏的推断),这要靠 expert load 监控发现。

理解这些延迟特征让你能正确设 SLA、做容量规划、调度负载——而不是盲目套用 dense 模型的经验。


19.13 部署 V4 的"半年时间表"

把 V4 部署到生产是一个长跨度的工程项目。给一个典型的"6 个月时间表"作为规划参考:

月 1:基础设施准备

  • CUDA 与编译器升级到各库要求的交集(见 §19.8 问题 1);装 NVSHMEM
  • H100 / B200 集群验收(NVLink / IB 网络测试)
  • 编译 FlashMLA / DeepGEMM / DeepEP 三个库
  • vLLM / SGLang 升级到包含 V4 适配的版本

月 2:单机功能验证

  • 单机 8 卡 H200 / B200 跑 V4 Pro(8 卡 H100 装不下,见 §19.15 Q1)
  • prefill 测试(256K / 1M context)
  • decode 测试(短 prompt 长输出)
  • MTP 投机解码功能验证

月 3:单机性能调优

  • throughput 基准(不同 batch、context 长度组合)
  • 抽样复现 README 评测表中的任务,核对输出质量(README 只有质量评测,没有吞吐 / 延迟数字)
  • 调优 vLLM 调度器参数(chunked prefill、prefix caching 等)
  • 建立性能回归 baseline

月 4:多机部署 + 通信调优

  • 2 节点 16 卡 IB 部署
  • DeepEP 的跨节点路径调通
  • 测试不同 TP × EP 配置的性能
  • IB 网络抖动监控与告警

月 5:可观测性 + 监控

  • 部署完整 Prometheus / Grafana 监控
  • 接入业务层监控(错误率、延迟、用户行为)
  • 制定 SLA + 告警阈值
  • 演练 incident response 流程

月 6:生产灰度 + 全量

  • 从 1% 流量开始灰度
  • 与现有模型 A/B 对比输出质量
  • 逐步扩到 10% / 50% / 100%
  • 监控用户反馈与回归

这个时间表是本专栏按经验给的估计(没有统计出处),大致对应中等规模团队(5-10 人 ML infra + 2-3 人模型工程师)。大公司可能压得更短,小团队可能要更久。读者用本专栏做规划时,可以参考这个表设期望——避免"3 周上线 V4"的不切实际目标。


19.14 V4 生产事故的 6 类与应对

部署 V4 到生产后,可能遇到的事故类型与应对方案:

事故类型 1:单卡 OOM

症状:某 rank GPU 内存溢出,进程崩溃。 原因:长上下文 prompt 触发 KV cache 超预算,或 prefill 激活过大。(DeepEP buffer 是预分配的常驻显存,尺寸不够时表现为发起 kernel 前的断言失败而不是 OOM,见第 16 章。) 应对:限制 max_tokens、限制 max_concurrent_requests、调小 prefill 分块。 预防:监控 GPU memory peak,设阈值告警在 90%。

事故类型 2:跨节点通信中断

症状:IB 网络中断后某 rank 卡死,整个集群 hang。 原因:IB 卡硬件故障、交换机重启、网络拥塞。 应对:检测 hang,杀掉所有 rank 重启;切到备用集群。 预防:每 5 秒发心跳,检测到 hang 自动重启;监控 IB 错误计数。

事故类型 3:精度漂移

症状:模型输出与 reference 的偏差超出 golden test 设定的阈值。 原因:vLLM / DeepGEMM 升级后算子细节变化、GPU 硬件批次差异。 应对:回滚到上一个版本;如果无法回滚,先把流量切回仍在旧版本上的实例。 预防:每次部署前跑 golden test,差异超阈值阻止上线。

事故类型 4:MTP 接受率骤降

症状:投机解码接受率相对基线(V3 报告口径 85%–90%)大幅下滑。 原因:fine-tune 后 MTP 与主模型分歧、温度参数误改。 应对:临时关闭 MTP,回到逐 token decode;调查根因。 预防:监控 MTP 接受率,相对基线跌幅超过阈值即告警(阈值自行标定)。

事故类型 5:路由失衡导致单 rank 过热

症状:某 rank 的 expert load 远高于其他 rank,该卡先撞上功耗/温度墙。 原因:训练 bias 与生产数据分布不匹配;某些 expert 突然被过度激活。 应对:临时把流量分一部分到其他实例;检查 bias 配置。 预防:监控 per-rank expert load 分布,不均衡度超出基线即告警。

事故类型 6:vLLM 调度器队头阻塞

症状:请求队列堆积,新请求等待几分钟才被处理。 原因:长 prompt prefill 阻塞 decode 队列。 应对:开启 chunked prefill(前提是引擎已为 V4 实现分块 prefill,见 §19.3 PR 6)、限制单请求 max prompt length。 预防:监控 queue depth,超阈值降级。

每类事故都需要有明确的 runbook(应对手册)。V4 的工程团队大概率有内部的事故响应文档——本节只是给读者一个起点参考。


19.15 部署 V4 的"运维 Q&A 速记"

Q1: V4 Pro 单节点 8 卡 H100 跑得动吗?

跑不动。V4 Pro 的权重是 864.7 GB(64 个 safetensors 分片加起来的实测值,见第 15 章 §15.1),而 8 × 80 GB = 640 GB——光权重就装不下。

8 卡 H200(141 GB × 8 = 1128 GB)则可以:均摊每卡约 108 GB 权重,按 pin 版参考实现在 meta 设备上实例化是每卡约 117 GB(不切分的权重每卡各存一份,部分权重加载时提升成 FP32,见第 15 章 §15.1),剩二十来 GB 给 KV cache(按 max_seq_len=1048576 的部署设定,1M context 下每卡约 9.6 GiB / 序列)与 prefill 激活——能跑,但并发上不去。要舒服地跑长上下文 + 多并发,16 卡起步。

H100 这一档想单节点,请改跑 V4 Flash。

Q2: V4 Flash 比 Pro 省多少?

V4 Flash 总参 284B、13B 激活(README 口径),Pro 是 1.6T / 49B——参数量约 1/5.6、激活参数约 1/3.8。按 Pro 实测的每参数约 0.54 字节粗估,Flash 的权重约 0.15 TB 量级,单节点 8 卡完全放得开。

至于价格:README 里没有定价信息,本专栏不给 token 单价。真实成本请查官方定价页或按你自己的自建成本算——决定成本的是激活参数(影响算力)与总参数(影响显存),比例大致就是上面那两个数。

Q3: V4 在 A100 上能跑吗?

基本不能,而且卡点在计算而不是通信:

  • DeepGEMM 只支持 SM90 / SM100——A100 是 SM80,主路径直接不可用(仓库里 deep_gemm/legacy/ 有一批给 Ampere 用的 BF16 Triton 老 kernel,不覆盖 FP8 / FP8×FP4)
  • A100 没有 FP8 / FP4 的原生 tensor core 指令,V4 那套精度配方要靠软件模拟
  • 反倒是 DeepEP 支持 A100(README 明确写了 SM80),但只支持节点内(README 写明 "A100 support (intranode only)"),而且通信能跑不代表计算能跑

生产必须 H100 / H800 / B200 这一档。

Q4: 多机部署的网络要求?

DeepEP 的 README 测试平台是每卡一张 CX7 InfiniBand 400 Gb/s(网卡最大带宽约 50 GB/s,normal kernel 跨节点实测 RDMA 带宽 43–58 GB/s,测试卡是 H800)。README 说 DeepEP 在 InfiniBand 上充分测试过,RoCE 理论兼容但没有同等验证。另外别忘了 NVSHMEM 是硬依赖——不装就没有跨节点路径。

Q5: vLLM / SGLang 哪个更适合 V4?

在 V4 的引擎适配落地之前,这个问题没有实测依据。可以说的是两者的一贯定位:vLLM 生态更成熟、社区更大;SGLang 在结构化输出与前缀复用上更强。等适配落地后按你自己的 workload 实测。

Q6: 长上下文 OOM 怎么办?

  • 减小 max_model_len(如从 1M 减到 256K)——KV cache 大致按压缩段的长度线性缩
  • 减小 batch size:KV cache 是 per-sequence 的,batch 翻倍它就翻倍
  • 减小 prefill 的分块大小(需引擎支持分块 prefill):HC 让层间残差流是 4 路的,这部分激活是同尺寸普通 transformer 的 4 倍
  • 别指望"共享 KV 省显存":num_key_value_heads=1 意味着每个 TP rank 各存一份完整 KV,卡数越多总冗余越大

Q7: 如何 fine-tune V4?

V4 的训练栈未公开。要注意的是 QAT 这条约束(第 14 章):V4 的 routed expert 权重是在后训练阶段经 FP4 QAT 适应量化误差得来的(技术报告第 3.4 节),"反量化到 BF16 → fine-tune → 重新量化"这条常规路线会破坏这种适应性。具体工具链等社区释放。

Q8: V4 输出突然变差怎么办?

按优先级排查:

  1. 依赖库是否升过版(DeepGEMM / DeepEP / TileLang 的 kernel 细节变化会带来数值差异)
  2. 输入是否包含特殊 token(触发异常路径)
  3. GPU 是否 thermal / power throttle
  4. expert load 是否失衡(可能 fine-tune 或路由 bias 配置错)
  5. 采样参数是否被改动——官方对本地部署的建议是 temperature = 1.0, top_p = 1.0;Think Max 模式建议上下文窗口至少 384K

19.16 延伸阅读

  • vLLM 仓库的 V4 适配 issues / PRs(本章写作时尚未出现,以仓库实际进展为准)
  • SGLang 官方文档
  • TensorRT-LLM 官方文档
  • 本专栏第 5 章:FlashMLA 内部细节
  • 本专栏第 13 章:DeepGEMM 内部细节
  • 本专栏第 16 章:DeepEP 内部细节
  • 《vLLM 推理内核深度解析》专栏:vLLM 整体架构

19.17 本章小结

  • V4 部署需要解决 5 件事:引擎适配 / KV cache / 稀疏 kernel / MoE 通信 / 调度器
  • vLLM 0.8.5 不能直接加载 V4,本章推演的适配要动六处——架构注册 + KV cache 扩展 + FlashMLA + DeepEP + Indexer 接入 + 调度器
  • SGLang 路径相似,但 RadixAttention 与 V4 滑窗 KV 有特殊协调
  • TensorRT-LLM 适配预计较慢,主要给低延迟 + 高吞吐特化场景
  • 多机部署有 8/16/32 卡几种典型拓扑——TP × EP 配比影响吞吐
  • 部署常见 5 个问题:CUDA 版本、IB 配置、长上下文 OOM、精度一致性、MTP 启用
  • 上线 checklist 19 项必须全部通过

第 20 章是本专栏最后一章——把 V4 放在 2026 年开源 LLM 版图,做一次"现状与未来"的全景梳理。