关于在Hopper上使DeepSeek V4 Flash达到近200 tok/s的一些技巧
摘要
这篇博文提供了在双GH200工作站上使用vLLM对DeepSeek V4 Flash进行推理,达到近200令牌/秒的技巧和基准测试,重点介绍了使用Canada-Quant的量化检查点和张量并行优化。
我需要一个更智能的模型来搭建本地的Hermes Agent,于是换到了DeepSeek V4 Flash。首先,注意以下几点:
* 在vLLM上运行4个并发线程时,我可以达到约400 tok/s
* 400 × 60 × 60 × 24 × 30 大约是**每月10亿令牌!!!**
* DSv4Flash 每百万令牌成本为0.1966美元……我去……
* 为了生成价值约200欧元的令牌,我要花大约350欧元的电费。耶!
总之,为了少亏点钱,我花了一些时间优化DSv4Flash。通过使用这些[Canada-Quant的量化版本](https://huggingface.co/canada-quant/DeepSeek-V4-Flash-W4A16-FP8-MTP),并修补vLLM中的MTP代码,我在Hopper系统上达到了193 tok/s。详细信息请见博文。
查看缓存全文
缓存时间: 2026/06/08 17:21
# 2x GH200 用于 LLM 推理,第 2 部分:vLLM、DeepSeek V4 Flash 和 MTP
来源:https://dnhkng.github.io/posts/gh200-benchmarking-part-2/
## 引言https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#introduction
不久前,我为我的Hopper系统(https://dnhkng.github.io/posts/vllm-optimization-gh200/)针对MiniMax M2\.1进行了一些优化,随后又进行了一些更深入的GH200基准测试(https://dnhkng.github.io/posts/gh200-benchmarking/),我在其中将该机器视为一个内存洗牌系统进行测量。结果是一个简单的拓扑图:每个 Hopper 都有快速的本机 HBM,每个 Hopper 都有一条快速 NVLink C2C 路径连接到其自己的 Grace CPU,而两个 Hopper 之间的路径并不是普通的 GPU 对等链路。在此工作站上,跨 GPU 流量会通过 CPU 侧进行中转,速度远比本地 HBM 或本地 C2C 慢。
这就是后续内容(*因为一个更酷的项目而大幅延迟*)。这里的工作负载是***在 vLLM 中运行的 DeepSeek V4 Flash***,运行在双路 GH200 工作站上。简而言之,硬件表现与内存基准测试的预测完全一致。张量并行可以工作,但需要谨慎。官方检查点的速度比我预期的要慢。来自Canada-Quant(https://huggingface.co/canada-quant/DeepSeek-V4-Flash-W4A16-FP8-MTP)的量化检查点则快得多。为了使多 token 预测生效,我费了一些功夫,但一旦检查点和 vLLM 路径相互兼容,我获得了非常大的单流加速(*对于我笨重的本地 Hermes Agent 来说,bwHaHahahaaa...*)。
我测量的最佳单请求结果是,在 MTP3 下使用 Canada-Quant 模型检查点约为 193 个输出 token/秒,相比之下,未使用 MTP 时约为 106 个 token/秒。
***太长不看:如果你想在 Hopper 上运行 DSv4Flash,请使用 Canada-Quants 和我博客末尾的 PR。***
## 系统回顾https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#the-system-reminder
该机器是一台双路 Grace Hopper 工作站:
组件规格GPU2x Hopper H100,每块 96 GB HBM3CPU2x Grace,每块 72 核主机内存每块 Grace 480 GB LPDDR5X,总计 960 GBGPU 本地内存总计 192 GB HBM CUDA13\.0驱动580\.105\.08OSUbuntu 24\.04, aarch64
第 1 部分中的重要拓扑事实:
路径测量带宽本地 HBM约 3,700 GB/s本地 Grace LPDDR 到本地 Hopper约 377\-380 GB/s远端 Grace LPDDR 到 Hopper约 133 GB/sHopper 到 Hopper 中转拷贝约 57\-58 GB/s
对于 LLM 推理,这些数字提供了一个有用的思维模型。如果热解码路径从本地 HBM 流式传输权重,那么该机器会快得惊人。如果它反复穿越 GH200 间路径,那就更像是在用奶酪刨丝器自慰;你必须放慢速度,而且大多数人没有毅力坚持下去。如果引擎在批处理大小为 1 时产生大量 GPU 间流量,拓扑结构会暴露这一点。
## 我测试的内容https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#what-i-tested
主要目标是 vLLM 中的 DeepSeek V4 Flash(*是的,我也测试了 llama.cpp 及其Antirez(https://github.com/antirez/ds4)分支的 GGUFs,但速度慢了 5 倍*)。我测试了两个模型工件:
基准测试的形状特意设置得足够大,以避免仅测量启动和调度器噪声:
设置值提示长度8192 个 token输出长度1024 个 token提示数5最大并发数1张量并行度2`max_model_len`32768`max_num_batched_tokens`8192`max_num_seqs`1采样温度 0
主要的 vLLM 标志是:
`1 2 3 4 5 6 7 --distributed-executor-backend mp --tensor-parallel-size 2 --disable-custom-all-reduce --block-size 256 --gpu-memory-utilization 0.97 --kv-cache-dtype fp8 --generation-config vllm`
我还使用了:
`1 2 NCCL_P2P_DISABLE=1 VLLM_USE_FLASHINFER_SAMPLER=0`
在此机器上,`NCCL_P2P_DISABLE=1` 设置很重要。两个 Hopper 之间没有可用的直接对等路径,因此我希望 NCCL 避免尝试使用此处并不真正存在的拓扑。
## 第一个结果:Canada 量化版本快得多https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#first-result-the-canada-quant-is-much-faster
在不使用 MTP 的情况下,官方检查点和 Canada 量化检查点的性能不在同一个级别。
模型MTP 级别输出吞吐量官方 DeepSeek V4 FlashMTP064\.9 tok/sCanada W4A16/FP8 检查点MTP0105\.9 tok/s
这是一个巨大的差距,是我意想不到的 WTF,因为 W4A16/FP8 的模型大小比原始版本大 10 GB,而且综合来看,同一模型的小版本通常运行得更快。在此系统上,对于相同的单流基准测试形状,量化检查点大约快 63%。
这个结果不仅仅是文件大小的影响。在我的机器上,Canada 检查点实际上比官方检查点大:大约 159 GB 对比大约 149 GB。部分原因是它包含了 MTP 张量,并且将一些张量保留为 BF16 或 FP32。所以有用的问题不是“哪个目录更小?”,而是“哪些张量和内核位于热解码路径上?”
Canada 工件使用了不同的混合布局:路由专家权重是 W4 打包的,注意力投影是 FP8,一些敏感张量保持更高精度。对于此拓扑上的单流解码,这仍然可以减少活跃的专家权重流量,并使用更有利的 vLLM 内核路径,即使整个检查点目录更大。这使得结果与第 1 部分的内存测量结果一致,但这并不能证明加速仅仅来自磁盘上更少的字节。
官方模型并不“差”。只是这个精确的部署目标对于该工件来说不太宽容。在仅有 96 GB HBM 每 GPU 和弱 GPU 间路径的双路 GH200 工作站上,压缩检查点是更自然的选择。
有一个警告:这是吞吐量结果,不是质量结果。我预计 Canada 检查点在某些情况下可能比官方检查点稍差,因为路由专家权重的量化更激进。但该检查点并非粗暴的全模型 4 位转换。注意力投影保持 FP8,许多敏感张量保持 BF16 或 FP32,官方基线已经是一个压缩的 FP8 工件。我的预期是任何质量差异可能都很轻微,并且可能很难在有针对性的评估之外检测到。*我在这里还没有测量过。*
## MTP 曾出现故障https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#mtp-was-busted
多 token 预测应该有助于单流生成,因为它允许模型在每次目标模型步骤中提出多于一个 token。在实践中,我最初遇到了检查点/软件不匹配的问题。
Canada 模型是压缩的,但 MTP 块包含 BF16/未量化的张量,例如 `mtp.0.*`。vLLM 的 DeepSeek V4 NVIDIA O-投影路径期望在 MTP `wo_a` 投影上有 FP8 缩放元数据。这对于完全的 FP8 路径成立,但对此混合检查点不成立。
故障模式很简单:主模型可以加载,但 MTP 启动失败,因为草稿块没有预期的 FP8 缩放张量。
有两种方法可以解决这个问题:
1. 将 MTP 张量量化为与检查点其余部分相同的压缩张量格式。
2. 教 vLLM 为未量化的 MTP O-投影路径运行 BF16 回退。
第一个选项可能是更清晰的模型工件修复。但它需要对 MTP 张量进行真正的量化过程,并且可能需要校准数据和更多的模型处理。
第二个选项是一个有用的运行时修复。如果 MTP 块是 BF16,则将该小的草稿路径作为 BF16 运行,而不是要求 FP8 元数据。这就是我测试的内容。
还有一个额外的复杂性:在分享这些结果后,Canada-Quant 维护者指出,实际上需要三个相邻的修复才能在该工件上实现完全开箱即用的端到端 MTP。
1. 工件元数据需要匹配 vLLM 的运行时模块名称,而不仅仅是磁盘上的 safetensors 名称。MTP 仓库现在有一个基于正则表达式忽略模式的纯元数据修复。
2. vLLM 在构建 DeepSeek V4 MTP `e_proj` 和 `h_proj` 模块时需要传播 `prefix=`。没有这个,压缩张量会看到空的层名称并且无法匹配该模块。
3. 一旦加载和构建成功,NVIDIA O-投影执行路径对于未量化的 MTP `wo_a` 需要下面描述的 BF16 回退。
这是三个独立的故障点。缺少任何一个都会在的不同阶段中断运行:主模型加载、MTP 草稿构建或 MTP 草稿执行。
## vLLM 运行时修复(耶!)https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#the-vllm-runtime-fix-yay
补丁范围很窄。在 `vllm/models/deepseek_v4/nvidia/ops/o_proj.py` 中,vLLM 可以检查 `wo_a` 是否有 FP8 缩放元数据:
`1 2 3 4 5 6 def get_fp8_weight_scale(layer): if hasattr(layer, "weight_scale_inv"): return layer.weight_scale_inv if hasattr(layer, "weight_scale"): return layer.weight_scale return None`
如果缩放存在,则使用正常的 FP8 路径。
如果不存在,则在 BF16 中应用逆 RoPE,重塑平坦的分组 `wo_a.weight`,运行分组矩阵乘法,然后通过 `wo_b` 继续:
`1 2 3 4 5 6 7 8 9 10 11 12 13 weight_scale = get_fp8_weight_scale(wo_a) if weight_scale is None: # BF16/unquantized MTP fallback: # 1. reshape o to grouped heads # 2. apply inverse RoPE to the rope slice # 3. reshape to grouped wo_a input # 4. reshape flat wo_a.weight to [groups, o_lora_rank, input_size] # 5. z = einsum("bgi,gri->bgr", wo_a_input, grouped_weight) # 6. return wo_b(z.flatten(1)) return wo_b(z.flatten(1)) # Existing FP8 path.`
我还需要将 `hf_config_path` 传播到推测性草稿模型配置中。没有这个,目标可能使用清理过的/本地配置,但草稿路径可能回退到不同的配置源。这与上面的 `prefix=` 构建修复是分开的,但它属于同一类问题:草稿路径必须继承与目标路径相同的模型/配置上下文。
我为配置传播和 O-投影回退添加了有针对性的测试。对于上游 O-投影 PR,我将范围保持得更窄:仅 NVIDIA O-投影回退及其单元测试。该 PR 测试文件涵盖了缩放检测、BF16 逆 RoPE、分组的 `wo_a.weight` 重塑以及生产 `deep_gemm_fp8_o_proj` 回退分支。
这不是性能优化,而是一个兼容性修复,用于解锁 Canada 检查点中已经存在的 MTP 权重。
## MTP 结果 - 它使推理更快 🤯https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#mtp-results---it-makes-inference-faster-
一旦该路径生效,Canada 检查点随着 MTP 的扩展效果良好:
模型MTP 级别输出吞吐量平均 TPOT接受率Canada W4A16/FP8MTP0105\.9 tok/s9\.11 msn/aCanada W4A16/FP8MTP1152\.7 tok/s6\.20 ms90\.8%Canada W4A16/FP8MTP2179\.9 tok/s5\.18 ms81\.9%Canada W4A16/FP8MTP3193\.0 tok/s4\.82 ms71\.1%Canada W4A16/FP8MTP4162\.1 tok/s5\.81 ms47\.9%
MTP3 是本次运行的最佳点。MTP4 更差,因为额外的草稿深度得不偿失。接受率下降到约 48%,额外的草稿工作超过了收益,就像你已经饱了却还在吃最后几根薯条。
这是实际的经验教训:MTP 级别是一个调优参数,而不是单调的速度旋钮。更多草稿 token ≠ 更快。
在我发布这些数字后,yangsiqt2 (https://huggingface.co/canada-quant/DeepSeek-V4-Flash-W4A16-FP8/discussions/8#6a266e2e43643d62dc5c270e) 指出了另一个重要的注意事项:MTP 接受率对工作负载很敏感。他们的端到端 vLLM 基准测试使用了简短混合技术/代码/推理提示、64 个顺序请求和 256 个生成的 token。在该设置中,他们看到了比这个长提示、长输出基准测试更低的接受率:MTP1 约 81%,MTP2 约 63%。这仍然产生了很大的加速,但接受率不同。请自行分析!
## 官方检查点对比https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#official-checkpoint-comparison
官方检查点也从 MTP 中受益,但基线要低得多。
模型MTP 级别输出吞吐量官方 DeepSeek V4 FlashMTP064\.9 tok/s官方 DeepSeek V4 FlashMTP1108\.3 tok/s官方 DeepSeek V4 FlashMTP2138\.8 tok/s官方 DeepSeek V4 FlashMTP3149\.5 tok/s官方 DeepSeek V4 FlashMTP4134\.9 tok/s
形状类似:MTP 有帮助,然后过多的 MTP 开始有害。但在我测试的每个可比点上,Canada 量化检查点仍然更快。
> 此处最佳官方结果约为 149.5 tok/s。最佳 Canada 结果约为 193.0 tok/s。
## 重新审视 GH200 内存测量https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#gh200-memory-measurements-revisited
第 1 部分测量到本地 HBM 约为 3.7 TB/s,而中转 GPU 间路径仅为约 58 GB/s。这个巨大的比率正是我们试图调优推理系统以避免的。
双 GPU 张量并行解码工作负载只有在引擎保持通信相对于有用的本地工作较小时才有吸引力。Hopper 之间的每一次不必要的交换都在穿越系统中最弱的路径。流式权重字节的每一次减少都有帮助,因为单流解码期间机器仍然主要与内存移动作斗争。
Canada 量化检查点通过减少模型路径中移动的字节来提供帮助。MTP 通过将目标模型步骤摊销到多个被接受的 token 上来提供帮助。它们一起使工作负载更适合此拓扑。我不能仅仅将我的“2x GH200”视为一个大型 GPU。这是两个非常强大的 GPU+CPU 模块,它们之间的路径要弱得多。*真的很希望我能凑合出一个真正的 NVLink,那样我就可以做到了。但即使不是不可获取之物,那点硬件也要花费五位数。*
## 明显结论https://dnhkng.github.io/posts/gh200-benchmarking-part-2/#obvious-takeaways
我希望你没有通读整篇博客文章,它真的很无聊。你可以跳到这里,看到在这个双路 GH200 系统上,部署规则是:
1. 尽可能将活跃的解码工作保持在 HBM 中。
2. 避免不必要的 GPU 间流量。
3. 使用量化检查点,当它们减少活跃的权重流量而不破坏内核时。
4. 将 MTP 级别视为一个经过基准测试的参数。
5. 不要假设最深的 MTP 设置是最好的。
看,所有这些都很明显,对吧?有趣的部分是过程,以及让我浪费大约 2 天时间,这样你就能发现从 Canada W4A16/FP8 检查点配合 vLLM 中的 DeepSeek V4 Flash MTP3 可以获得出色的性能。测试的最佳配置是:
`1 2 105.9 tok/s without MTP 193.0 tok/s with MTP3`
让 MTP 工作使我的单流基准测试提高了 82%,我将其作为一个范围狭窄的上游 vLLM PR 提交:vllm-project/vllm#44847 (https://github.com/vllm-project/vllm/pull/44847)。
相似文章
Deepseek V4 Flash 在两块 Nvidia 4090d 48G (ada) 上以 vLLM 运行,速度约 105 t/s
技术文章:详细介绍如何使用自定义Triton内核和vLLM在两块Nvidia 4090d GPU上运行DeepSeek V4 Flash,在262k上下文环境下实现约105 tokens/秒的推理速度。
DeepSeek V4 Flash(98GB)在单块4060 Ti加CPU上本周速度提升300% [从2t/s到7t/s]
DeepSeek V4 Flash(98GB)现在通过CPU卸载,在单块RTX 4060 Ti上运行速度可达每秒7个token,相比上周的2t/s提升了3倍。
DeepSeek-V4-Flash W4A16+FP8 结合 MTP 自推测:在 2 张 RTX PRO 6000 Max-Q 上以 524K 上下文长度实现 85 tok/s
这篇文章详细介绍了一个经过定制并量化的 DeepSeek-V4-Flash 模型版本,启用了 MTP 自推测功能。通过修改后的 vLLM 设置,在双 RTX PRO 6000 Max-Q GPU 上实现了显著的速度提升。
针对双GH200优化的DSv4-Flash:SGLang上PP达10,000 tok/s,TG超过300 tok/s
DeepSeek V4 Flash在双GH200工作站上的详细基准测试,比较了SGLang和vLLM在1M上下文长度下使用DSpark投机解码的表现,发现SGLang更快,约为317 tok/s对比276 tok/s。
DeepSeek-v4-Flash-Mini 54GB GGUF 运行速度约 20.5 t/s
一个社区构建将 DeepSeek-V4-Flash 压缩为 54GB 的 IQ2_XXS GGUF 变体,采用激进的 2 位量化,在本地硬件上实现了约 20.5 tokens/s 的速度,同时大幅降低了显存/内存占用。