Qwen3.5-122B on DGX Spark · part 2
[Benchmark] Qwen3.5-122B 在 DGX Spark 加速 100%!
❯ cat --toc
TL;DR
Part 1 把 Qwen3.5-122B-A10B(hybrid-GDN MoE,NVFP4,76GB)跑上 128GB 的 DGX Spark,卡在 GDN 牆、結論是「等上游」。這篇是我不等了:換掉 vLLM、改用 Atlas 引擎跑同一顆解禁權重,17 → 33.9 tok/s(開 MTP 36.5,約 2×),解禁行為完整保留。 中間也順手確認了一條路走不通:花一個下午 build 剛 merge 的 vLLM PR #44700(B200 上 ~1.93x microbench、+24%),在 sm_121 上 16.9 tok/s 完全沒動 —— 它能加速是靠這台沒有的快 recurrent kernel。重點不是「17 量錯了」,是 17 只是 NVFP4-on-vLLM 這個工具箱裡的 local optimum。社群另一條 INT4-AutoRound 配方在 stock 模型上更跑到 ~51(3×)。先把數字量準,然後別只在一個工具裡找答案 —— 真正的 2× 在工具箱外。
同一顆 122B、同一台 GB10:換掉 vLLM,17 → 34 tok/s
白話版:一顆塞得進桌機、想得很慢的大腦 —— 換了引擎才變快
大型 AI 模型通常太大,桌機放不下。這顆 Qwen3.5-122B 有 1220 億參數,卻塞得進一台 NVIDIA DGX Spark —— 一台有 128GB 共用記憶體的桌上型 AI 小盒子。它載得起來、答得出問題、甚至會用工具(查天氣、跑多步驟任務)。帳面上很猛:一顆接近 SOTA 的大模型,就跑在你桌上。
問題是慢。它每秒大概吐 17 個字的量 —— 讀是讀得了,但就是那種「等聊天回覆」的慢。所以我想加速,用了 vLLM 團隊剛 merge 的一個官方改動,他們說正好能讓這類模型快一倍。結果我花了一整個下午把它 build 出來,速度一模一樣:17 變 16.9,根本沒動。
原因很簡單:那個「2x」加速只吃得到我這張 GPU 根本沒有的硬體單元。模型本來就走在一條慢的小路上,那次升級只是把它根本不會上的高速公路重鋪了一遍。教訓很明顯 —— 花一個下午升級之前,先確認你真正卡住的地方,是不是那次升級要修的那個。
但事情還沒完。那個加速其實存在 —— 只是不在我當時用的工具裡。當我不再調 vLLM,改用另一個推理引擎(Atlas)跑同一顆模型,速度直接翻倍。那個下午不算白花,但真正該做的不是翻牆,是繞過去。
第一次看? 如果「MoE」「GDN」「NVFP4」還很陌生,量化演算法到底在做什麼 講量化、TurboQuant 實測 講 KV cache 壓縮。這篇講的是一面硬體牆 —— 一種模型架構,在這顆特定的 GPU 上根本沒有快的 kernel —— 以及兩種繞過去的方法。
128GB 小盒子有意思的地方,不是「什麼跑得快」,是「什麼居然跑得起來」。122B 本來就不該塞進桌機,結果它真的塞得下,光這件事就有點爽。Part 1 把這顆跑上桌機,卡在 14 tok/s,結論是「沒 flag 能修,等上游幫 sm_121 寫 GDN kernel」。這篇是後來的事:我沒等到上游,但等到了別的 —— 一個不靠 vLLM 的引擎,把同一顆權重直接翻倍。另外還有一篇姊妹篇寫 DeepSeek-V4-Flash(284B,同一台,但從另一個方向撞上同一類牆),會另外發。
下面的數字全是我自己這台、這個禮拜量的(GX10 / GB10 / sm_121,128GB 統一記憶體,driver CUDA 13.0)。
17 tok/s 是天花板,而且是硬體缺 kernel 不是沒調好
穩定生成大概落在 17 tok/s。這就是 vLLM 下的天花板,調 flag 動不了它。
卡點是那些 GDN 層。vLLM 的快速 GDN prefill backend(FlashInfer / CuteDSL)只在 datacenter Blackwell(sm_10x)和 H100 上才會被選到 —— 那些晶片有快路要用的 tcgen05/TMEM tensor memory 指令。GB10 是 sm_121(compute capability 12.1),不在那張清單上,所以 vLLM 退回 Triton/FLA。17 tok/s 就是這條 fallback 的上限。(講清楚:GDN 不是「沒那些指令就跑不了」—— Triton 那條路跑得好好的 —— 只是這台選不到快的那條。)
小確幸是:Triton 沒我想的那麼爛。我本來估 ~14 tok/s,實際 fallback 跑 17,還贏了同一台上 DeepSeek-V4-Flash 的 13.6。Triton kernel 再怎樣也還是 GPU kernel。
這裡「牆」的意思要講清楚。它不是「快的路很慢」,是「這台根本選不到快的路」—— 那些優化過的 GDN backend 在這個 SM 不會跑,所以上游再怎麼優化它們,這台也吃不到。這個點先記著,等下 #44700 那段要用。
開 MTP 反而更慢:15.2 < 17
這顆模型自帶 MTP(multi-token prediction)draft head,官方 recipe 也教你開 speculative decoding:
--speculative-config '{"method":"mtp","num_speculative_tokens":6}'
在原廠的 2× RTX 6000 Pro(TP=2)上這是賺的。在單顆 GB10、跑 vLLM 上是賠的:15.2 tok/s,比 17 還低。
原因跟我日常那顆 35B 開 MTP 會變慢是同一類。Speculative decoding 是拿多餘的算力/頻寬去減少 sequential step 的 overhead。但單卡又卡頻寬又卡 GDN,根本沒多餘的東西可以換 —— draft 再 verify 的開銷只是純加 latency。用 NVFP4 已經把頻寬省到差不多了,MTP 想再拿多餘的頻寬來換速度,也沒得換。(這條也先記著 —— 它不是對「每個引擎」都成立,等下 Atlas 那段會翻案。)
abliteration 拿掉的是「拒絕」,不是「學會的講法」
這個「heretic」abliteration,有些地方真的解禁了、有些地方真的沒解,而有意思的正是這條分界線。
- 情色 / NSFW:完全開。不加 disclaimer、不囉嗦,而且文筆不差。
- 撬鎖之類:它會答,但前面會掛一段 disclaimer —— 比較軟、只解了一半。
- 政治敏感(例如 1989 六四):還是吐官方那套說法。破綻在 reasoning 那段,模型會自己講到要「兼顧合規性」「參考官方紀念日的措辭」。
所以 abliteration 拿掉的是拒答傾向 —— 模型不再說「這我不能幫你」—— 但它拿不掉訓練時學進去的回答框架。模型在訓練時學會了「某些題目該怎麼講」,這件事是寫死在 weights 裡的,光把拒絕打掉碰不到它。對照一顆 abliteration 做得更狠的 DeepSeek-V4 變體,它連政治敏感題都直接答:差別在 abliteration 推得夠不夠用力、以及 base 模型的審查是怎麼實作的 —— 不是「中國 base 模型所以解不開」這麼簡單。
這件事跟速度也有關。社群那些更快的路,大多是給 stock 模型用的。所以讓這面牆一直站著的問題,對我來說不只是「什麼更快」,而是「什麼更快、而且還吃得了我這顆解禁權重」。這點記著,下面兩段要用。
vLLM PR #44700 在這台等於沒做:還是 16.9 tok/s
先講死路。vLLM PR #44700 —— 標題是「拆開 prefill+decode 混合 batch,把 decode 導去 recurrent kernel」—— 已經 merge,在 B200 上測出約 1.93x kernel microbench、端到端 +24%。一顆卡在 GDN 層的 122B 模型 —— 這看起來根本就是為我準備的解藥。我把它 build 出來。
結果:16.9 tok/s。跟 17 一樣,扣掉量測誤差。完全沒變快。
為什麼:這個 PR 的加速,來自把 decode batch 導去快的 recurrent kernel。但在 GB10 上,那個 recurrent 路徑就是 Triton/FLA fallback —— sm_121 沒有快的 native 版本(見前面 17 tok/s 那段)。所以再怎麼導,decode 還是落在慢 kernel 上,端到端數字紋風不動。那個 +24% 是真的 —— 在 B200 上,那裡有快的 recurrent kernel。在 sm_121 上它什麼都不是。
講白一點:GB10 上的 GDN 牆是「快的路沒在這台蓋」,不是「那條路很慢」。 一個改善「怎麼併進快車道」的 PR,救不了一台根本沒有那條快車道的機器。只要我還待在 vLLM 裡,122B 在 GB10 就是 17 tok/s,vLLM 再怎麼升都一樣。
而最後那半句,正是我困了一個下午的陷阱:只要我還待在 vLLM 裡。
解法在 vLLM 外面:換引擎,或換格式
這段我差點沒寫,因為我第一個結論是「17 就是天花板,等上游幫 sm_121 寫出快的 GDN kernel」。這結論沒錯 —— 但只在「NVFP4 + vLLM 這套工具箱裡」成立。我花一整個下午想用一個 vLLM PR 翻過牆,其實該做的是走出去。有兩扇門。
同一顆 122B、同一台 GB10:vLLM 卡 15–17,換 Atlas 跳到 33.9 / 36.5(約 2×),社群 INT4-AutoRound 在 stock 模型上報 51。
門一 —— 換引擎:Atlas 實測 33.9 tok/s(開 MTP 36.5),約 2×。 Atlas 是一個 Rust 寫的推理引擎(Avarok-Cybersecurity/atlas),目前專攻單一硬體 —— GB10 / DGX Spark / sm_121。我直接餵我本地那顆解禁 NVFP4 122B —— 不用重量化、不用重下載。它載得起來、沒報錯,關鍵在我這顆 checkpoint:heretic 是原生 Qwen3.5、GDN 層留在 plain BF16、沒被打包成量化格式,Atlas 能直接讀這種未量化的 linear-attention 層(我推測它跟 vLLM 一樣不去動這些層,但這點我沒在 Atlas 文件找到明寫的精度策略,是從它載得起來反推的)。我實測(GB10,丟掉 warmup、穩態同 prompt ×3):baseline 33.9 tok/s(33.9/34.0/33.9 極穩),開 MTP(K=2)36.5 —— 差不多是 vLLM 那 17 的 2×,而且是同一顆解禁權重、解禁行為完整保留 —— NSFW、政治敏感全過。但講清楚:數字是量到的,只是我還沒把它弄成一個穩定、可重現的日常配方。
注意剛剛那條 MTP 規則發生什麼事。在 vLLM 下,MTP 讓 122B 更慢(15.2 < 17);但我實測 Atlas 時 MTP 還是賺的 —— 36.5 vs baseline 33.9,雖然只有 +7.6%(SSM hybrid + bandwidth-bound,增益本來就小)。同一顆模型、同一張卡 —— speculative decoding 划不划算其實要看引擎。「別開 MTP」從來不是硬體定律,是 vLLM 在這台上那條路的性質。
先說清楚,這 2× 有但書:
- 還沒弄成穩定的日常配方。 數字量得出來,但要變成天天能開的設定還要再花工夫。
- context 目前卡在 4096。 76GB 權重載完只剩 ~12GB,KV pool 要手動調才不會 OOM:
--max-prefill-tokens 2048、--max-batch-size 1、--gpu-memory-utilization 0.99、--max-seq-len 4096。要推到 16–32K 有機會,但還沒測。 - 我這 ~34–37 還輸社群測到的 ~46–48(stock 122B 上 Atlas),大概是 vision encoder 加 heretic 的額外開銷,我還沒跑 base-vs-MTP 的 A/B 把這個差距補掉。
所以我先把 Atlas 擺著、還沒當日常 —— 但方向站得住:那面我本來以為「永久」的牆,被一次換引擎打開了一條路,而且是用我自己的權重、解禁照樣全過。
門二 —— 換格式:INT4-AutoRound 社群測到 ~51 tok/s(3×),但是 stock。 有一個社群配方 —— INT4-AutoRound 權重、INT8 的 LM head、再加 FlashInfer 上的 MTP —— 在 GB10 上跑到 ~51 tok/s(NVIDIA 論壇 thread)。卡點就是 abliteration 那段講的:那是 stock 權重。要用我的解禁模型達到那速度,我得自己再做一次 AutoRound 量化,而我還沒做。所以 51 是我看得到、但還沒走過的那扇門 —— 一個社群量到的數字,不是我的。
所以 17 tok/s 是天花板嗎? 在 vLLM 是,換個啟動器就未必 —— 同一顆權重、同一台機器,Atlas 直接翻倍(順帶一提,同一個引擎跑 Qwen3.6-35B 還能上 90+ tok/s)。我差點就停在「17 是天花板、等上游」那個結論;真正讓我翻案的不是更好的 flag,是定時去收新工具、新資訊。情報跟得上,牆才會變成門。
升級踩坑 reel:build 反咬我四次
我那個下午,大半時間是在跟 build 搏鬥,不是在跟模型搏鬥。四個坑,每個都可以再用:
1. prebuilt wheel 默默把 torch 換成 CPU 版。 裝一個 rolling 的 prebuilt vLLM wheel(d20260607.cu133)時沒加 override,因為 FlashInfer 沒鎖 torch 版本,uv 就直接把它解析成 torch 2.10.0+cpu —— 完全沒 CUDA。解法是鎖死它:
uv pip install vllm-*.whl --override torch==2.11.0+cu130 # ← 留住有 CUDA 的 torch
2. 我下了一個錯的 NO-GO,後來交叉檢查時被抓出來。 我一度判「torch 2.10 + cu133 不存在,死路」。丟給 Codex(它會去讀 primary source)反駁掉:那個 wheel 其實是用 torch 2.11.0+cu130 compile 出來的 —— .cu133 只是 build-base 的標籤 —— 真正的差別是 ABI 層級的 symbol export(CUDAStream::query() 這顆在兩個 torch 版本之間 export 行為變了)。我的 NO-GO 是看標籤下的,不是看 ABI。教訓記下來:NO-GO 結論動手前先拿去對原始檔驗一次。
3. vision tower 上的 PTX 牆。 cudaErrorUnsupportedPtxVersion,死在 ViT 的 attention:_vllm_fa2_C 這顆 flash-attn-2 kernel 只帶了 cu133 的 PTX、沒有 sm_121 的 cubin,所以 CUDA 13.0 的 driver JIT 不出來。主要的 decode 路(flashinfer + GDN)沒事,只有 encoder 卡住。我這樣修,讓 encoder 繞開 fa2:
--mm-encoder-attn-backend TORCH_SDPA # ViT 改走 torch 原生 attention
4. runner 那層 Docker stage 沒繼承 timeout。 只有 base stage 有 UV_HTTP_TIMEOUT,runner stage 在從 pypi.nvidia.com 下載 517MB 的 cublas wheel 時 timeout。解法是 runner stage 也補上(ENV UV_HTTP_TIMEOUT=600)。
這一串沒讓模型變快。它讓我得到一個能跑的 build,而這個 build 證明了模型在 vLLM 裡快不起來 —— 這本身也是一種結果。
收穫
最花時間的地方。 幾乎都不是模型,是 build:torch 的解析、PTX/cubin 對不上、wheel 標籤的誤會。真正的 benchmark —— 載入、生成、數 token —— 只花幾分鐘。反而是那個「想讓它變快、結果沒變快」的嘗試,吃掉一整個下午。而真正讓它變快的那件事 —— 換一個引擎跑 —— 花的時間比 build 還少。
之後還能沿用的診斷方法。
- prebuilt CUDA wheel「裝得起來」但推論很慢或卡在 CPU,先看
torch.version.cuda—— 一個沒鎖好版本的 transitive dependency,就會在背景偷偷把你降成+cpu,還不報錯。 - 某個子系統(例如視覺)報
cudaErrorUnsupportedPtxVersion、但主路載得起來,代表那顆 kernel 只附了比你 driver 新的 toolkit 的 PTX。把那個子系統導去原生 backend 就好,不用整包重 build。 - NO-GO 結論在動手之前,先拿去給一個會讀原始檔的人(或 AI)驗。build-base 的標籤(
.cu133)不等於 ABI。
通用原則,分兩半。 第一:升級之前先問,你的瓶頸在不在那次升級要優化的那條路上?#44700 的加速來自把工作導去一顆 sm_121 跑不到的快 kernel,所以它在這台結構上就不可能幫到你 —— 不管它在 B200 上那個 +24% 多真。看你實際在跑的那條路的數字,不要看 changelog。第二,而且是我撞了才學到的:撞牆的時候,先分清楚是「那條路」有問題,還是「整個工具箱」有問題。17 tok/s 是個對的數字,但推論是錯的。解法不是 vLLM 裡的某個 flag —— 是換一個引擎,或換一個格式。
結論
如果你在 GB10 / DGX Spark(sm_121)上跑 hybrid-GDN 模型:
- 塞得下、跑得動。 Qwen3.5-122B-A10B NVFP4(約 76GB)在 vLLM 下 serve、會用工具(
qwen3_coderparser)、寫得了沒審查的內容。 - 17 tok/s 是 vLLM 的天花板,不是這台機器的天花板。 GDN 走 Triton/FLA fallback,快的 GDN backend 在 sm_121 選不到。待在 vLLM 裡,這數字不會動。
- 2× 的出口在 vLLM 外面。 Atlas 實測同一顆解禁權重 33.9 tok/s(開 MTP 36.5,約 2×),還沒弄成穩定日常配方。社群的 INT4-AutoRound 配方在 stock 模型上跑到約 51 —— 換格式,前提是你願意重量化。
- MTP 看引擎,不是硬體定律。 在 vLLM 下更慢(15.2 < 17);在 Atlas 下是加速的一部分。別把一個 flag 在某引擎的行為套到所有引擎。
- 別在 vLLM 裡追 GDN 加速 PR。 #44700 的 +24% 來自把工作導去一顆 sm_121 跑不到的快 recurrent kernel,在這台完全沒加速(16.9)。槓桿在 vLLM 外,不在裡面。
- abliteration ≠ 完全解禁。 它拿掉拒絕,不拿掉訓練進去的講法。而且它會縮小你的快路選項,因為社群多數加速都是給 stock 模型的。
這面牆是真的:待在 vLLM 裡,122B 在這台就是 17 tok/s,升一個下午也不會變。但這面牆有門。量測沒錯,是搜尋空間太窄。換引擎,或換格式 —— 別一直調那個慢的。
常見問題
- 128GB 的 DGX Spark 跑得動 122B 模型嗎?
- 跑得動。Qwen3.5-122B-A10B 是 hybrid-GDN MoE,NVFP4 量化後約 76GB,塞進 GB10 的 128GB 統一記憶體還留得下 KV cache 的空間。在 vLLM 下能 serve、能正確呼叫工具、能跑多步 agent,大概 17 tok/s。我也實測了另一個引擎(Atlas),同一顆權重 baseline 33.9 tok/s、開 MTP 36.5(約 2×),只是還沒弄成穩定的日常配方。
- 為什麼 Qwen3.5-122B 在 vLLM、GB10 上只有 17 tok/s?
- 它的 Gated DeltaNet(GDN)linear-attention 層在 GB10 的 sm_121 上沒有快的 kernel 可走。vLLM 的快速 GDN backend 只在 datacenter Blackwell(sm_10x,有 tcgen05/TMEM tensor memory 指令)上才會被選到;sm_121 退回 Triton/FLA。17 tok/s 就是這條 fallback 的天花板,其實還比原本預測的 14 快一點。
- 有沒有比 17 tok/s 更快的方法在 DGX Spark 上跑 Qwen3.5-122B?
- 有,但不是靠調 vLLM。我實測了 Atlas(Rust 寫的推理引擎),同一顆解禁 NVFP4 122B 用同一份權重 baseline 33.9 tok/s、開 MTP 36.5(約 2×),解禁也照樣全過 —— 只是還沒弄成穩定可重現的日常配方(context 目前也卡 4096)。社群另一條 INT4-AutoRound + INT8 LM head 的配方跑到約 51 tok/s,但那是 stock 模型、也不是我跑的。重點是:vLLM 的 NVFP4 17 tok/s 是一個 local optimum,真正的出口在 vLLM 外面 —— 換引擎或換量化格式,不是調 vLLM。
- vLLM PR #44700 在 DGX Spark 上有用嗎?
- 沒用。PR #44700(標題是「拆開 prefill+decode 混合 batch,把 decode 導去 recurrent kernel」)在 B200 上測出約 1.93x kernel microbench、端到端 +24%。它的加速來自把 decode 導去快的 recurrent kernel —— 但在 sm_121 上那個 recurrent kernel 就是 Triton/FLA fallback,不是快的 native kernel。所以再怎麼導,decode 還是落在慢 kernel 上,數字沒動:17 變 16.9,完全沒變快。