如果你在 GB10 上用 vLLM 跑 DeepSeek-V4-Flash,投機解碼應該開。CyberQ 在雙機 GB10 中測試到的加速是 1.44–1.57 倍,代價之一是延遲變得不可預測。但這篇要講的重點並不是開投機解碼加速這件事,而是我們碰到官方版不支援,改用社群配方,但社群配方沒寫的兩個坑,以及一個我們自己記錄在案後,又被推翻的預測。
CyberQ 本次測試的硬體是兩台 DGX Spark(GB10,sm_121,每台 128GB 統一記憶體),中間一條 QSFP28 DAC 直連,實測 iperf3 99.0 Gbit/s、NCCL all-reduce busbw 12.19 GB/s(線速的 97.5%)。
兩台跑 vLLM 的跨機 TP=2,模型是官方 deepseek-ai/DeepSeek-V4-Flash-0731,167 GB / 48 shards。
實測數值參考
同一支 bench、同一組協定(/v1/completions、貪婪、ignore_eos 固定 800 token、暖機一次後取三次),只切換投機解碼:
| 組態 | decode 中位數 | 組內散布 | draft 接受率 |
|---|---|---|---|
| 無投機 | 26.57 tok/s | 0.96% | — |
| DSpark(5 draft tokens)× 4 次重跑 | 38.28 / 39.97 / 40.34 / 41.65 | 6–27% | 30.2–37.7% |
| DSpark,第一次執行 | 69.30 | 36.5% | 64.8% |
那個 69.30 是離群值,不要拿它規劃容量,重跑四次之後,典型值穩定落在 38–42,接受率 30–38%。加速倍率約莫是 1.44–1.57 倍,而不是首次量到的 2.61 倍。
值得注意的是散布,無投機那組測試成績的散布只有 0.96%,而投機組是 6–27%。CyberQ 實測認為,模型本身的解碼速度非常穩定,所有的不確定性都來自 draft 接受率,而草稿模型提出資料在主模型的接受率取決於生成內容的可預測性。所以投機解碼開起來後我們獲得的並不是更穩定更快速,而是平均跑起來更快、但單次則不保證的效果。
對互動式對話如 Open WebUI 這種介面來說,開這個是划算的,但對需要可預測延遲的批次工作,得自己再權衡一下。
一般的 vLLM 跑不起來
CyberQ 原本用vllm/vllm-openai 映像(0.26.1rc1),這款映像在同樣兩台機器上跑 Qwen3-235B-A22B、Mistral-Small-4-119B、Qwen3.8-27B 都沒問題,跨機 TP=2 一切正常。但 DeepSeek-V4 起不來,能力檢查說支援,編譯好的 kernel 說不支援。
改用社群版本才能跑
官方版的開不起來,因此換成有 sm_121 kernel 的映像就可以,社群上至少有三組專案是做出來的:
| 來源 | 映像 | 回報 |
|---|---|---|
| MiaAI-Lab | ghcr.io/anemll/dspark-vllm-gx10:0.1.1 | 單聊 62–83 tok/s |
| hazyumps | hazyumps/deepseek-v4-flash-gb10:sm121-cu130-…(jasl/vllm fork) | 40–60 tok/s |
| elsung | aidendle94/sparkrun-vllm-ds4-gb10:production-v2 | ~41 tok/s 單流 |
CyberQ 後來選用 MiaAI-Lab 的方案,它的關鍵在recipe/overlay/vllm/models/deepseek_v4/nvidia/sm120.py,用覆蓋檔換掉整個模型實作,並且 DeepGEMM 走執行期 JIT 編譯而非預編的 sm90/sm100 二進位。
值得一提的是,把三家的數字跟我們的重跑並排之後,elsung 的 ~41 最接近我們的典型值,而 62–83 那個範圍對應到我們的離群值。這不是說誰的好誰的不好,而是說這個工作負載的變異就是這麼大,單次量測在這裡沒有意義。
配方沒寫但會踩到的兩個坑
一、docker save | ssh docker load 會丟掉 registry digest。
配方的 .env 用 digest 釘住映像,這是對的做法,兩台跑不同版本是最難查的故障。
但如果你像我們一樣,是在一台上 docker pull 之後用 docker save | ssh … docker load送到第二台(這樣可省一次網際網路的下載),那台的映像只會保留 image ID,不會有 RepoDigests,於是啟動腳本開起來後會報 No such image 的錯誤。
因此要確認兩台的 image ID 一致(docker images --no-trunc)之後改用 tag 即可。
二、JIT 快取不能放在共用儲存上。
compose 把 VLLM_CACHE_ROOT 寫死在 HF cache 掛載點裡面(/cache/huggingface/vllm-cache)。這在「每台各有一份 HF cache」的前提下沒問題。
但我們為了不重複存放 167 GB,把 HF cache 放在共用的 NFS 上,結果兩個 rank
同時 JIT 編譯到同一個目錄,worker 會掛在這裡:
RuntimeError: Assertion error (/workspace/.deps/deepgemm-src/csrc/apis/../jit_kernels/impls/../../jit/compiler.hpp:147): runtime != nullptr
而搶贏的 head 活得好好的,只是接下來每 60 秒印一次No available shared memory broadcast block found。
這是比較難找出問題的一種 error 型態,GB10 的雙機 AI 算力叢集變成只起來一半。
解法呢,需要把 JIT 快取改成每台本機目錄(我們用 /jitcache 加一個 bind mount),編完各約 6.6 MB。兩台日誌裡的 Profiling CUDA graph memory 那行必須一模一樣,跑起來才是正確的。
一個要更正的預測
我們先前在同一組 GB10 雙機硬體叢集上測了三款不同廠商的 LLM 模型,本來有歸納出一條兩項式:
每 token 時間 = 權重位元組 / 518 GB/s + 層數 × 0.217 ms
係數只用 Qwen3.8-27B 與 Mistral-Small-4 兩點解出,而 Qwen3-235B-A22B 完全沒有參與擬合,預測 24.0 對實測 23.83,誤差 0.7%。518 GB/s 正好是兩台 GB10 記憶體頻寬的總和。
在量測 DeepSeek-V4-Flash 之前,我們用它推出無投機應該有 35.1 tok/s(活躍位元組 9.93 GB/token 是拆 safetensors 標頭算的,routed experts 是 NVFP4,但 attention 與 shared experts 在 quantization_config.ignore 裡、沒有量化)。
結果實測是 26.57 tok/s,高估 24.3%。
若保留每層 0.217 ms 的項反推,這款的等效頻寬只有 351 GB/s,是前三款模型的 68%。可能的原因我們沒有逐一驗證,這款跑在不同的 vLLM build(anemll 的 0.25.2.dev0,而係數是在 0.26.1rc1 上擬合的),MLA 與 hyper-connection 的每層計算不是純頻寬項能夠描述的,活躍位元組的估算也可能低於 MoE 實際的記憶體存取量。
誠實說明這條算式在同一款映像、三種架構上內插準到 0.7%,換 build 又換架構之後高估 24%。它是同組態下的內插工具,不是跨 build 的預測工具。
所以,該開投機加速嗎?
確實在這個架構該開,1.44–1.57 倍是實際有加速的,而且不需要額外記憶體(draft 模型只有 96 個參數張量,出廠就附在 checkpoint 裡),這點和之前做另一個模型測試的情況又不太一樣,但如果要成功實作起來,請照著這幾點做:
不要用單次量測規劃容量。我們第一次量到 2.61 倍,重跑四次都是 1.5 倍左右。接受率從 30% 到 65% 都出現過。
無投機的基準也要量。沒有它,你不知道投機到底幫你換到多少加速效益,也無法判斷某次變慢是接受率掉了還是別的問題。
注意延遲的可預測性。 投機把散布從 1% 拉到 6–27%。互動式對話划算,需要穩定延遲的場景要自己權衡。
量測要用同一支腳本、同一組協定。 這篇所有數字都出自同一支 bench,這是跨組態比較能成立的唯一前提。









