2026-05-29 — 次浏览
一个 Triton FP8 绕道补丁,为 DGX Spark 的 GB10 挤出多 17% 的 NVFP4 速度
一个社区补丁把 NVFP4 权重改走 GB10 的 FP8 张量核心,而非缓慢的 BF16 后备路径,让 Qwen3.6-35B-A3B 在 DGX Spark 上从 40.8 提升到 47.6 tok/s。
NVIDIA 把 DGX Spark 营销成一台原生支持 FP4 的 Blackwell 机器——但本月一篇社区实测揭示了这个说法背后的一个小注脚,以及一个巧妙的 80 行绕道方案,把营销所暗示的吞吐量补回了大半。简短版本是:Spark 的 GB10 芯片的张量核心其实无法直接执行 FP4 运算,因此 NVFP4 模型一直在走一条缓慢的软件后备路径。一个 Triton monkey-patch 把它们改走芯片的 FP8 核心,于是 Qwen3.6-35B-A3B 从 40.8 跃升到 47.6 tok/s。
GB10 对 FP4 究竟做了什么
NVIDIA DGX Spark 的 GB10 Superchip(Grace Blackwell,SM121)并没有原生 FP4 硬件。这点很关键,因为 vLLM 在拿到 NVFP4 量化模型时,一直把矩阵乘法导向一条缓慢的 BF16 反量化后备路径(Marlin path),而完全没有用到芯片的 FP8 张量核心。4-bit 权重在 GEMM 执行前会被重新解包回 16-bit——这几乎抵消了量化的整个意义。你省下了内存,但运算路径并不比跑一个更大的格式快。
这个 80 行的 Triton 绕道
一篇实测文章(发表于 2026 年 4 月 22 日,5 月 6 日更新)详述了一个约 80 行的 Triton monkey-patch,在不分叉 vLLM 的情况下解决这个问题。在模型加载时,补丁把 NVFP4 权重转成 FP8;在推理时,它通过 torch._scaled_mm 在 GB10 的 FP8 张量核心上执行 GEMM。由于它是一个运行期 monkey-patch,因此不需要修改 vLLM 源码——你导入它,它就替换掉相关的 kernel,现有的 NVFP4 checkpoint 便突然走上了快速路径。
作者也标出了反量化公式中一个关键更正:你必须除以全局 scale,而不是乘以它,以避免数值溢出。这正是那种会产生垃圾输出或 NaN、而不是干净崩溃的「一个字符符号错误」,因此对任何想复现这个补丁的人都值得特别提醒。
数据
在 Qwen3.6-35B-A3B(NVFP4)上,单流生成从 40.8 tok/s 提升到 47.6 tok/s——加速 17%。作为对照,同一台机器上的原生 FP8 checkpoint 的上限是 53.8 tok/s,因此这个补丁补回了缓慢后备路径与专门打造的 FP8 模型之间的大部分(但非全部)差距。
| 路径 | Qwen3.6-35B-A3B 单流 |
|---|---|
| NVFP4 走 BF16 后备(Marlin) | 40.8 tok/s |
| NVFP4 走 Triton FP8 绕道 | 47.6 tok/s(+17%) |
| 原生 FP8 checkpoint(上限) | 53.8 tok/s |
此实测在 vLLM 0.19.1 搭配 NVIDIA 驱动程序 580.142 上运行。
实务笔记
如果你在 Spark 上自建,并因为 NVFP4 checkpoint 能塞进统一内存而一直选用它,这值得花十分钟。诚实的决策树是:如果你的模型有原生 FP8 checkpoint 而且塞得下,它在 53.8 tok/s 仍是最快的路径。但 FP8 权重大约是 FP4 的两倍大,在一台每一 GB 统一内存都要争抢的机器上,对较大的模型而言这可能根本不是选项。此时 Triton 绕道能让你在保留较小 FP4 占用的同时,达到 FP8 上限约 12% 以内的水准——这是一笔真正划算的交易。把你的软件栈钉在 vLLM 0.19.1 与驱动程序 580.142,以对齐测试过的组合,并在信任输出前再次确认「除而非乘」的更正。
较少被讨论的角度
这里更深层的教训,是关于如何解读这款硬件上的 Blackwell 营销话术。「原生 NVFP4 的 Blackwell」在格式层面上技术上为真——芯片理解这个编码——但 GB10 的张量核心并不会直接执行 FP4 运算,因此这个 4-bit 格式只有在软件把它桥接到一条硅芯能真正跑得快的路径(例如 FP8)时才会有回报。在一台 3000 美元级、tok/s 与统一内存余裕就是全部胜负的机器上,一个无需分叉 vLLM、仅 80 行的用户层补丁就能补回逼近原生 FP8 的差距,这是一记尖锐的提醒:在 Spark 上,决定真实吞吐量的是 kernel 与运行期层——而非标签上的量化格式。格式告诉你权重有多小;它没告诉你它们跑得有多快。