~/blog/part2-qwen-122b-14-toks-gdn-kernel-gap

Qwen3.5-122B on DGX Spark · part 1

[vLLM] Qwen3.5-122B 跑起來了。但只有 14 tok/s。

2026-03-192 分鐘閱讀#dgx-spark#sm121#qwen3.5-122b#vllmEnglish

前言

這篇講的是一個看起來像失敗、但其實不是的結果。

套用第一篇的四個 SM121 bug 修法之後,Qwen3.5-122B 在 DGX Spark 上正常運作。輸出正確。模型可以用。但速度穩定在 14 tok/s——大約是 gpt-oss-120B 在同一台機器上的四分之一。

怎麼調都不動。這就是這篇的故事。

用個比喻:想像一條十二線道的公路,六條是鋪好的柏油路,六條還是泥土路。車子在跑——沒有壞掉——但一半的車道在工程速限裡爬行。硬體沒有問題。架構裡一半的層(GDN 層)缺少最佳化的軟體支援。你沒辦法靠調整駕駛方式來修一條還沒鋪好的路。


預期的效能目標是什麼?

目標很單純:用 Qwen3.5-122B-A12B-NVFP4 當 DGX Spark 的主力模型。122B 參數,NVFP4 量化,128 GB 統一記憶體——算法沒問題:122B × 4-bit ≈ 61 GB,剩下空間給 KV cache。gpt-oss-120B 在同一台機器上能跑 59 tok/s,所以對 122B NVFP4 的預期也在這個量級。也許稍慢一點——模型更大,架構不同——但應該在同一個數量級。

14 tok/s 不在預期裡。


在找到根本原因之前,試了哪些設定調整?

套用第一篇的修法(SM121 PTX 路徑、Marlin thread race、SupportsQuant、排除 CUTLASS_FP4)之後,模型正常載入,輸出正確。前幾個 token 出得很快,然後吞吐量穩定在 14 tok/s。

把能調的全試了:

  • 拿掉 --enforce-eager — 本來就沒有,不是這個問題
  • KV cache dtype — fp8,跟 gpt-oss 一樣,沒有差別
  • --max-num-batched-tokens — 上下調整,幾乎沒有影響
  • VLLM_MXFP4_BACKEND=marlin — 已設定,啟動 log 確認有讀到
  • --moe-backend marlin — 已設定
  • GPU 記憶體使用率 — 0.90,標準值

14 tok/s 沒有動。這就是「調參問題」變成「架構問題」的時刻。


效能瓶頸真正在哪裡?

Qwen3.5-122B-A12B 是混合架構,不是標準 Transformer。它把兩種層混在一起:

  • 標準 attention 層 — 一般的 Transformer MHA/MLA,Marlin 有針對這種計算形狀特別最佳化的 kernel(可以想像成:有專用工具)
  • GDN(Gated Delta Network)層 — SSM 風格的遞迴層,必須保持 BF16

Marlin 對標準 linear 層和 MoE expert GEMM 有快速的特化 kernel。對 GDN,沒有。每個 GDN GEMM 都走 generic fallback 路徑——能跑,但慢。

這也解釋了第一篇的 SupportsQuant 修法為什麼不能省:沒有它,vLLM 會把 GDN 層量化成 NVFP4,GDN 的遞迴 hidden state 損壞,輸出變成垃圾。修法讓 GDN 保持 BF16——正確的行為——但代價是 GDN 跑在沒有特化 kernel 的路徑上。

在 GB10 的 273 GB/s 頻寬下,算法很直接:搬動 61 GB 的 weights 一次需要約 0.22 秒。GB10 上 122B NVFP4 的理論 decode 上限約 30 tok/s。要達到這個上限,每一層都需要高效的 kernel。當一半的架構(GDN)走 generic fallback,吞吐量往一半的上限平均——14 tok/s 就是這個平均的落點。


得到了什麼

結果是 14 tok/s,不是理論上限的 ~30 tok/s。但這個過程沒有白費。

確認了:

  • 第一篇的 SM121 fix stack 對 Qwen3.5-122B 是有效的。輸出正確。
  • SupportsQuant 是必須的——沒有它,GDN 層被量化,輸出垃圾。這個 bug 不直觀,從零開始診斷要花好幾個小時。
  • GB10 上 122B NVFP4 的理論 decode 上限是 ~30 tok/s。等 Marlin 加入 GDN kernel 後,這是要驗證的目標值。

確立了:

  • 14 tok/s 是目前的下限,不是 bug。沒有任何設定錯誤。
  • 瓶頸是軟體缺口(Marlin 缺少 GDN kernel),不是硬體限制。
  • GB10 有足夠的算力跑 Qwen3.5-122B。問題完全在軟體堆疊。

診斷模式: 如果 Qwen3.5-122B 在 SM121 上跑很慢,你已經設了 VLLM_MXFP4_BACKEND=marlin--moe-backend marlin,啟動 log 確認顯示 Using backend: marlin,輸出也正確——你碰到的就是 kernel 缺口。停止調整。等 kernel 來了數字才會動。


結論

套用第一篇的修法後,Qwen3.5-122B 在 DGX Spark 上正確運作。14 tok/s 不是故障——是目前 Marlin 所能支援的軟體上限。

現在如果需要在 GB10 上互動使用:gpt-oss-120B 59 tok/s 是可用的選項。Qwen3.5-122B 14 tok/s 適合離線批量任務,對延遲要求不高的場景。

如果你是因為 Qwen 跑很慢才找到這篇:你沒有漏掉任何 flag。你已經到達上限了。等 Marlin GDN kernel 來了再回來看。

常見問題

為什麼 Qwen3.5-122B 在 DGX Spark 上套用所有 SM121 修法後仍只有 14 tok/s?
Qwen3.5-122B 是混合架構,包含必須保持 BF16 的 GDN(Gated Delta Network)SSM 層。Marlin 對標準 linear/MoE 層有特化 kernel,但 GDN 沒有。每個 GDN GEMM 走 generic fallback 路徑。在 GB10(273 GB/s)上,這使吞吐量平均到 ~14 tok/s,而不是理論上限的 ~30 tok/s。
Qwen3.5-122B 在 DGX Spark 的 14 tok/s 是設定錯誤還是 kernel 缺口?
是軟體 kernel 缺口,不是設定錯誤。如果你已設定 VLLM_MXFP4_BACKEND=marlin、--moe-backend marlin,且啟動 log 確認顯示 'Using backend: marlin',你已到達上限。停止調整,等 Marlin 加入 GDN kernel 支援。
GB10 上 Qwen3.5-122B NVFP4 的理論 decode 上限是多少?
~30 tok/s。算法:122B × 4-bit ≈ 61 GB,273 GB/s 頻寬下搬動 61 GB 需要 ~0.22 秒。所有層都有最佳化 kernel 時,上限約 30 tok/s。目前缺少 GDN kernel,吞吐量平均到 ~14 tok/s。
在 DGX Spark 上,現在應該用 gpt-oss-120B 還是 Qwen3.5-122B?
互動式使用選 gpt-oss-120B,~59 tok/s。Qwen3.5-122B 的 14 tok/s 適合對延遲要求不高的離線批量任務。等 Marlin 加入 GDN kernel 支援後再重新評估 Qwen3.5-122B。