~/blog/dgx-spark-nvfp4-compression-not-compute

DGX Spark · part 32

[Benchmark] NVFP4 在 DGX Spark 比 FP8 快 1.5 倍——但贏在壓縮,不是那顆 FP4 運算單元

2026-05-304 分鐘閱讀#nvfp4#fp8#dgx-spark#gb10English
cat --toc

TL;DR

DGX Spark (GB10, SM121, 273 GB/s) 單流 decode,純 dense Qwen3-8B 上 NVFP4 比 FP8 快約 1.5 倍:FP8 25.65 tok/s、NVFP4 W4A4 38.59、NVFP4A16 W4A16 40.85(N=3 中位、kv-cache fp8)。快的是頻寬,不是算力——最快那條(W4A16)把 FP4 權重解壓回 BF16,根本沒用到 FP4 tensor core。我之前在 hybrid 的 Qwen3.6-35B-A3B 上看到 FP8 NVFP4,那是不能量化的 BF16 GDN 層稀釋掉格式紅利,不是 NVFP4 的問題。原則:FP4 的提速 ∝ 它真正壓得動的權重 bytes。

白話版:4-bit 不是「算得快」,是「檔案小」

NVFP4 是一種把 AI 模型壓很小的格式——每個權重只用 4 個 bit,是 FP8 的一半。直覺上「更小應該更快」沒錯,但快在哪很多人會搞錯。

DGX Spark 這台 NVIDIA 的桌上型 AI 主機,跑模型時真正卡住的是「把資料從記憶體搬進晶片的速度」,不是「晶片算得多快」。所以模型檔小一半,每吐一個字要搬的資料就少一半,於是變快。這跟晶片裡那顆專門算 4-bit 的硬體單元(FP4 tensor core)其實沒關係——我實測最快那條路根本沒用到它,只是把 4-bit 權重解壓回一般格式,再用普通硬體算。

還有個但書:這個「變快」只算在壓得動的那部分。純 dense 模型整顆都能壓,所以快;但有些混合架構的模型有一大塊壓不動,FP4 就幫不上忙,反而 FP8 比較快。所以答案不是「NVFP4 比較快」,是「看你那顆模型有多少壓得動」。


前言

四個月前我幫一台 DGX Spark 換機油,發現它的引擎電腦是別款車的——那是 Part 1 的故事(SM121 ≠ SM120)。Part 19 接著下了結論:「NVFP4 在 GB10 上是陷阱,FP8 快 32%。」

那個結論是錯的——更準確地說,是被別的變因混進去了。Part 19 測的是 Qwen3.6-35B-A3B,一顆 hybrid MoE,裡面有一大塊不能量化的 BF16 層。這篇我換成純 dense 模型重測,把那個變因拿掉,結論翻過來了。這是收穫,也是一次關於「乾淨對照」的教訓。

目標:NVFP4 慢,是格式問題還是 kernel 問題?

NVFP4 在 GB10 上不如預期,可能有兩種原因,意義差很多:

  • 格式問題:4-bit 這個格式在這顆晶片上本質就不划算。
  • kernel 問題:格式沒問題,是軟體還沒成熟,沒把硬體榨乾。

Part 19 分不清楚,因為它在一顆 hybrid 模型上測——而 hybrid 架構自己就帶了一個混淆變因。要回答這題,得先找一顆純 dense transformer,讓「這層能不能量化」不再是變數。

為什麼換成純 dense:hybrid 的 BF16 地板會吃掉 FP4 紅利

GB10 單流 decode 卡在記憶體頻寬(273 GB/s):每吐一個 token,速度約等於「每 token 要搬的 bytes ÷ 頻寬」。NVFP4 把權重壓一半,理論上 decode 該快一倍——但只有「壓得動、而且每步真的會搬到」的權重,才吃得到這個好處。

Qwen3.6-35B-A3B 是 hybrid:它的 GDN(Gated Delta Network)SSM 層對精度敏感,必須留 BF16,被排除在量化外。我之前的 profile 顯示這些 BF16 層吃掉約 59% 的 decode 時間。所以不管你把可量化的 MoE 專家壓成 FP8 還是 FP4,那 59% 動都沒動——FP4 紅利被稀釋,FP8 靠成熟 kernel 反而贏。

純 dense 沒這層地板:整顆都能壓。所以我抓了 Qwen3-8B(dense),量化三個版本來比。

結果:兩種 FP4 版本都贏 FP8 約 1.5×

同一顆 dense base、單流 decode、N=3 取中位、kv-cache fp8、400 tokens。為了拿到 W4A4 真正的 cudagraph 數字,這組跑在一個能編譯 sm_121a FP4 kernel 的舊 stack 上(vLLM 0.20.2 + FlashInfer 0.6.9 + cutlass-dsl 4.4.2)。

FormatRepoGen tok/s(中位)Kernelvs FP8
FP8 (W8A8)RedHatAI/Qwen3-8B-FP8-dynamic25.65Cutlass FP81.00×
NVFP4 (W4A4)RedHatAI/Qwen3-8B-NVFP438.59FlashInfer Cutlass NVFP4(FP4 MMA)1.50×
NVFP4A16 (W4A16)ELVISIO/Qwen3-8B-NVFP4A1640.85Marlin-family(解壓回 BF16)1.59×

dense 上格式真的有用:權重 bytes 砍半,吞吐 ~1.5×。我「壓縮就該變快」的假設成立了——前提是先把 hybrid 那層污染拿掉。

先講個但書:W4A16 這顆是別家(ELVISIO)出的,跟 FP8/W4A4 的 RedHatAI 不同,所以「W4A16 比 W4A4 快 6%」這條可能是量化 recipe 的差,不純是 kernel。這條當參考就好。站得住的是:兩種 4-bit 都比 8-bit 快約 1.5×。

最快那條路,根本沒碰 FP4 運算單元

這點我每次看到都還是覺得反直覺。GB10 上唯一真的在 tensor core 上跑 FP4 運算的是 W4A4——結果它排第二(38.59)。冠軍 W4A16(40.85)是把 FP4 權重解壓回 BF16,做一次普通的 BF16 matmul。它只把 FP4 當壓縮檔用,從頭到尾沒點燃 FP4 ALU。

NVFP4 在這台的故事,一句話講完:用了那顆 FP4 運算單元的路,輸給無視它、只把 FP4 當小檔案的路。 batch=1 時卡的是頻寬,而 FP4 tensor core 給你的是「算得多」——那不是瓶頸。要看到它發揮,得高併發,而單人聊天不是。

cudagraph ≈ eager:速度不是 graph capture 來的

我第一次在現役 stack 上比時,W4A4 只能用 --enforce-eager 跑(它的編譯路徑壞了,後面說),FP8 跟 W4A16 卻走 cudagraph。這比法不公平,值得查一下到底差多少。

幾乎沒差。W4A4 用 enforce-eager:39.62 tok/s;W4A4 在能 cudagraph 的 stack 上:38.59。在雜訊內。而且 FP8 / W4A16 換了兩套完全不同的 toolchain,數字也幾乎沒動(FP8 26.27 → 25.65、W4A16 41.64 → 40.85)。在 bandwidth-bound 的 workload 下,cudagraph 主要省的是 kernel launch overhead;但單流 decode 幾乎吃不到這塊。花一天重建 stack 去「修」eager 之前,先知道這件事比較好。

CUTLASS #3227 在 4.5.1 修好了——但對速度沒差

一開始 W4A4 根本編不起來:image 裝的是 cutlass-dsl 4.5.0,它對 sm_121a 的 FP4 MMA 生出非法 PTX(ptxas fatal: Unexpected instruction types for '_mma')——CUTLASS #3227。所以我先退回沒這 bug 的舊 stack(FlashInfer 0.6.9 + cutlass-dsl 4.4.2 + vLLM 0.20.2)量 W4A4,再回頭驗那個最直接的修法。

乾淨的修法是 cutlass-dsl 4.5.1:它原生把 sm_121a 放進 admissible_archs(不用再手動 patch),W4A4 在它上面直接編到 b12x cudagraph 路徑(FlashInferB12xNvFp4LinearKernel、no enforce-eager、ptxas 錯誤 0 個)。vLLM 的 b12x 整合(PR #40082) 已進 v0.22.0 pre-release,dispatch 在近期的 0.21.1 dev build 也有了。

重點來了:在 4.5.1 上重測,W4A4 是 38.22 tok/s——跟舊 stack 的 38.59 差在 1% 內,還是輸 W4A16 的 40.41。 這個修法解鎖的是「能乾淨編譯」,不是「變快」。bandwidth-bound 的盒子上,就算 FP4 MMA kernel 編得好,也突破不了 W4A16 解壓路徑同樣會撞的記憶體天花板。所以「得 pin 舊 stack」這個麻煩沒了——而它原本可能威脅到的結論(FP4 算力對單流毫無幫助),反而在現役 toolchain 上被釘得更死。

收穫

最花時間的地方:重建舊 stack 去拿 W4A4 的 cudagraph 數字——結果那是最不重要的一個。它產出的跨-stack 對照在方法上很髒(vLLM/FlashInfer/cutlass 一次全換)。救回它的是副作用:它證明了 cudagraph ≈ eager,而那才是真正有用的結論。

可搬走的診斷方法:量化格式表現不如預期時,把「格式」「kernel」「模型」三件事拆開。先跑一顆純 dense transformer、全部跑在同一套固定版本的 stack 上,再去看任何 hybrid 或 spec-decode 路徑的數字。Part 19 那個「陷阱」結論會出包,就是因為跳過這步,讓別的變因混了進來。

通用原則:少幾個 bit 能換到速度,只限「你真的壓得動、而且每步會搬到」的那些 bytes——不包含 hybrid 模型留在 BF16 的那塊,也不靠一顆 bandwidth-bound 工作根本用不到的運算單元。

結論

DGX Spark 單人聊天,截至 2026-05-30:

  1. dense 模型上 NVFP4 比 FP8 快約 1.5×——Qwen3-8B 單流實測。用它。
  2. 贏在壓縮,不在算力。 W4A16(解壓)是最快路徑,沒碰 FP4 cores。單流別去追那顆 FP4 ALU。
  3. hybrid MoE(例如 daily 的 Qwen3.6)上,FP8 還是贏——BF16 GDN 層稀釋掉 FP4。看模型挑格式,不是看格式。
  4. 別為了「讓 W4A4 不 eager」去重建 stack。 這台 bandwidth-bound,cudagraph ≈ eager。
  5. 編譯後的 FP4 路徑在 cutlass-dsl 4.5.0 是壞的(CUTLASS #3227);4.5.1 修好了——W4A4 原生編到 b12x cudagraph。但我在 4.5.1 上重測,速度一樣(38.22,還是 ≤ W4A16):這個修法給的是乾淨編譯,不是吞吐。

範圍:8B dense、單流(batch=1)、GB10。高併發 serving 是另一個情況——FP4 算力在那裡可能有用,我還沒測。


本系列其他篇:Part 1 — 為什麼你的 DGX Spark 只會輸出「!!!!!」 · Part 19 — NVFP4 在 GB10 上是陷阱 · Part 25 — Nemotron 3 Nano 衝到 74 tok/s

常見問題

NVFP4 在 DGX Spark (GB10) 上有比 FP8 快嗎?
純 dense 模型、單流 decode 下有,快約 1.5 倍。Qwen3-8B 上我量到 FP8 25.65 tok/s、NVFP4 38.59–40.85 tok/s。但看模型:像 Qwen3.6-35B-A3B 這種 hybrid MoE,反而 FP8 贏,因為不能量化的 BF16 層佔了每個 token 大部分要搬的資料。
NVFP4 有用到 GB10 的 FP4 tensor core 嗎?
只有 W4A4 那條有,而且它不是最快的。GB10 上單流最快的是 W4A16——它把 FP4 權重解壓回 BF16 再用 BF16 算,完全沒碰 FP4 運算單元。NVFP4 在這台的價值是檔案小(省頻寬),不是 FP4 算得快。
為什麼 NVFP4 在 dense 上快、在 hybrid 上慢?
GB10 在 batch=1 decode 卡的是記憶體頻寬。NVFP4 只對「壓得動的那些 bytes」有幫助。純 dense transformer 整顆都能量化,砍一半權重 bytes 直接換到速度。hybrid 模型的 GDN/SSM 層留在 BF16——Qwen3.6 上那塊佔了約 59% 的 decode 時間,FP4 動不了它。
cudagraph 對 GB10 的 NVFP4 decode 重要嗎?
幾乎不重要。W4A4 用 enforce-eager 跑出 39.62 tok/s,換到能 cudagraph 的 stack 是 38.59,差在雜訊內。bandwidth-bound 的工作下,cudagraph 主要省的是 kernel launch overhead,而單流 decode 根本不會曝露那個 overhead。