
前傳:這兩台機器的雙機 RoCE 直連、KV 池與跨機張量平行的前提,都在前一個系列的 Day 27|雙 DGX Spark GB10 合體與分工合作的部署與實測。這篇是同一台機器的續集。
這篇系列的前段文章是以封裝為主,如Day 3 說 image 要鎖 digest,理由是上游的 tag 會被移動。今天用一個真實案例示範同一件事的另一面。當你的服務不只是官方 image,還疊了一層社群 patch,上游一發正式版,patch 會壞在哪裡、怎麼判斷哪些還要留、怎麼證明搬過去的東西跟原來一樣。這也是本系列開始中段文章後,第一次真的動到這兩台 DGX Spark。Day 9 算出來兩個模型擠在一台時餘裕只剩 6 GB 不能當平時組態,今天的實驗就緒後可用記憶體只剩約 2 GiB,這是地端 AI 環境偶爾會碰到的情形,本文的第五節與第六節會說明這個數字。
環境:兩台 GB10(各 128 GB 統一記憶體),以 RoCE v2 直連
程式碼:ivanusto/,釘在 tag <tag>。差異檔在 patches/,掛載的九個檔案在 files/,量測條件另見 RESULTS.md。
vLLM 0.30.0 這個著名 AI 推論引擎平台的 GitGub 發行說明上面寫著支援 DeepSeek V4.1 Flash。看到這行字,合理的期待是,拉官方映像、指向權重、開跑。實際在兩台 GB10 上試,會發現距離「開跑」還有九個檔案的距離。
這篇記錄的是把一份社群配方從 2026-09-11 的 nightly 版搬到官方正式版的完整過程。
會寫下來,是因為過程中遇到的每一個問題都不是這個模型獨有的,而是每個模型會碰到的,當「上游改版之後,既有 patch 怎麼搬」這件事的通則。
重點先放在這邊
熟悉地端 AI 推論部署的讀者,可以跳過這節,這段是給較不熟悉地端 AI 推論的人獲得一些心得用的。
DeepSeek V4.1 Flash 這個新模型現有的幾個版本,有兩個特別之處,決定了移植的難度。
第一,它有一組叫做 Engram 的查表權重,每張卡分到約 47 GiB。在一般伺服器上,可以把這張表放在主機記憶體,需要時再讓 GPU 讀過去。但在統一記憶體架構的機器上,「主機記憶體」和「顯示記憶體」是同一池,這個做法一點好處也沒有。
社群配方的解法是讓這張表留在磁碟,每一步只讀當下用到的那幾列,在 CPU 上解量化後送進 GPU。
主權重約 100 GiB 加上這 47 GiB,本來就塞不進 128 GB。
第二,跨兩台機器做張量平行時,注意力的 kernel 是按「每張卡分到幾個頭」預先編譯的。
兩台機器的分法和四台機器不同,把一份為四台寫的 patch 直接拿來用,只會得到一模一樣的錯誤訊息。
原版引擎只認得它自己支援的量化格式,社群常用的 EXL3 不在其中。加上兩台機器的容量上限,能選的其實很少:
| 候選 | 格式 | 大小 | 可否用原版引擎 |
|---|---|---|---|
| Mia-AiLab EXL3 2.9bpw、3.0bpw、coolbho3k 3bpw | EXL3 | 197 到 400 GiB | 否,需要配方自帶的載入器 |
| nvidia NVFP4 與第三方 FP8 | 原生格式 | 475 到 491 GiB | 格式可以,但兩台放不下 |
| LibertAI REAP-256E(384 個專家剪到 256) | 專家 MXFP4、其餘 FP8 | 196 GiB | 可以 |
只有最後一個同時滿足格式與容量。代價是品質,作者在模型卡上明白寫出剪枝讓文字困惑度上升約 14.1%、
圖片說明上升 8.9%,而且該配方本身把上下文限制在 32K。這兩點在動手前就要想清楚,因為它們決定了移植成功之後,這個配方能不能真的拿來用。
還有一個細節容易漏掉:REAP-256E 的倉庫沒有附 Engram 的那兩個分片(編號 47 與 48),要從原始模型取得。
如果機器上已經跑過同一個模型的其他量化版本,那兩個分片通常就在本機,可以直接接進來,省下數十 GB 的下載。
新版把模型程式的目錄改了名,並把 Engram 相關的程式拆成兩個檔案。
原配方的做法是「準備好整個檔案,啟動時覆蓋掉映像裡的同名檔」。目錄一改,這種掛載要嘛掛到不存在的路徑,
要嘛掛上去的是舊版程式,把新版的修正一起蓋掉。後者特別危險,因為它不會報錯。
做法:不要沿用整檔覆蓋。找出原配方的差異(多數專案會附 diff),逐一評估每一段在新版還需不需要,再套到新版檔案上。
套 patch 工具在上下文對不上時,會降低比對嚴格度去盡量套上。其中一段就這樣被塞進了另一個函式的 return 敘述中間:
alignment=(
576
if uses_fp8_ds_mla_layout and not _use_v41_mxfp8_kv_record()
if _dsv41_indexer_sm12x(): # 被塞進來的兩行
return DeepseekV4IndexerSM12xBackend
else 512
),
)
這段之所以被抓到,是因為它剛好造成語法錯誤。如果落點再偏一點,變成語法合法但語意錯誤,就會一路帶到執行期,最後表現成某個難以解釋的品質問題。
解法
允許模糊比對,但每一個勉強套上的段落都要仔細看過,套完務必做一次語法檢查。順帶一提,同一個 patch 引用的類別在新版也被改了名,要改成繼承新的基底類別。
原配方有一段是強制改用某支 top-k kernel,因為預設的那支在這張卡上會超出共用記憶體。
新版把這個選擇做成了正式參數,一個啟動旗標就能指定。
解法
移植前要先查上游收了什麼。已經收進去的就刪掉,做成可設定項的就改用參數。
這樣的好處是,每刪掉一個 patch,之後每次改版就少一份維護成本。
vLLM #57028(zhao6300 提出,截至 2026-09-23 仍未合併)
處理的正是這張卡的分頁幾何問題。它的修改和原配方有一段重疊,但另外還帶了兩個空批次的保護。
我的做法是重疊的部分沿用原配方(因為那份已經在實機跑過),另外兩個保護從上游 PR 取用。
解法
把「已經被實機驗證過」和「上游認為正確」當成兩個不同的來源,逐段挑選,不做二選一。
注意力 kernel 依「每張卡的頭數」預先產生。兩台機器分到的頭數是四台的兩倍,而函式庫沒有內建這個尺寸。
原配方附了修改原始碼的 patch ,但映像裡有預先編譯好的產物,會讓即時編譯整個跳過, patch 形同不存在。
解法
把預編目錄用空目錄遮蔽,逼它走即時編譯,編完後用符號表確認目標 kernel 真的存在。
不要看到建置成功就當作完成,因為建置本來就會成功,只是產物是舊的。
這段是移植裡唯一需要重寫的部分。原本的實作和舊版程式糾纏在一起,新版的結構反而更清楚,查表的類別已經把怎麼配置權重與怎麼取用權重拆成可以覆寫的方法,所以磁碟模式可以做成一個選項,不去動共用的程式路徑。
真正的問題是怎麼知道自己在 CPU 上重寫的解量化,和 GPU 上那支 kernel 算出來的完全一樣?
解法是寫一支對照測試,用真的安全張量檔當輸入,同時跑兩條路徑,比較輸出:
驗收標準訂在逐位元相同,不是誤差夠小。這類查表沒有浮點累加順序的問題,本來就該完全一致。
如果只要求接近,就會把真正的缺陷放過去。實測兩個切片都逐位元相同,這才算可信。
值得一提的是,這個模式必須關掉 CUDA graph,因為每一步都要回到主機讀檔。程式裡直接在圖擷取時丟出明確的錯誤,不讓它默默產生錯誤結果。
移植後的組態
官方正式版映像,加上九個檔案的修改與一支自行編譯的 kernel,兩台機器張量平行,以 RoCE 直連,32K 上下文,關閉 CUDA graph,開啟投機解碼。
啟動情形
主權重 48 個分片從網路檔案系統載入 22.6 分鐘,投機解碼的草稿模型再掃一輪約 44 分鐘,加上剖析與暖機,合計約 69 分鐘可服務。KV 池 372,529 個 token。
功能全部 pass:
效能
單路解碼每秒 9 到 10 個 token,八路併發總吞吐每秒 31.5 個,暖啟動首個 token 延遲 0.92 秒。解碼期間 GPU 使用率 83% 到 96%,屬於計算受限。這個數字只有配方作者公布值的一半左右,原因沒有再追。
數字若不附條件就沒有意義,這節把這次量測的條件寫清楚,方便判讀與重現。
軟硬體
| 項目 | 值 |
|---|---|
| 機器 | 兩台 GB10,各 128 GB 統一記憶體,以 200 Gb 等級網路直連,NCCL 走 RoCE v2 |
| 引擎 | 官方映像 vLLM 0.30.0(aarch64),torch 2.13.0+cu130,FlashInfer 0.6.18.post1 |
| 驅動 | 580.178.04。與配方作者的基準(580.173.02)不同,所以跨配方比較時這是一個未受控的變因 |
| 權重 | REAP-256E,固定於撰稿時的最新修訂版,Engram 分片取自原始模型 |
| 啟動參數 | 張量平行 2、32K 上下文、最大併發序列 8、單批 4096 token、GPU 記憶體比例 0.90、關閉 CUDA graph、投機解碼 k=5、純文字模式、思考預設關閉 |
測試腳本的來源
測試條件
這次量到的限制,以及沒有做的事
不建議當正式服務用,理由有三個:
那為什麼還要做?因為它回答了一個原本大家只能猜的問題,也就是當上游雖然正式支援了這個模型,但在這張卡上還沒到開箱即用,缺的是雙機的 kernel 實體化與分頁幾何修正。等上游把這兩塊補上,這份移植就是現成的起點。
在那之前,這篇實作,是實務上跑這條路線時,用戶會碰到缺什麼的具體證據。
這次移植站在幾份公開工作上,沒有它們不可能在一個晚上完成:
社群配方的價值不只在能跑,更在於作者願意寫下限制。上面幾份都清楚標明了自己的代價,方便我們實作時能評估。
本 repo 以 Apache-2.0 釋出,與 vLLM 一致,各來源的歸屬見 NOTICE。
Day 26 之後談變更管理時會用 ITGC 的形狀寫變更單,這一節先用同一個形狀把這次移植記下來。前六列是移植的規模,後四列是變更單一定要有而技術文章常常漏掉的部分。
| 項目 | 內容 |
|---|---|
| 變更前的基線 | 社群配方 Libertai/dsv41-flash-vllm-2x-spark,對應 vLLM 2026-09-11 nightly,配方作者的驅動基準 580.173.02(待填:配方 repo 當時的 commit) |
| 變更目標 | 官方映像 vLLM 0.30.0(aarch64),torch 2.13.0+cu130,FlashInfer 0.6.18.post1,本機驅動 580.178.04 |
| 原配方 patch | 7 個。直接沿用 3 個,人工修正後沿用 1 個,完全重寫 2 個(Engram 讀磁碟與載入器過濾),因上游已支援而刪除 1 個 |
| 另從上游未合併 PR 取用 | vLLM #57028 中 3 個檔案的片段,截至 2026-09-23 該 PR 仍未合併 |
| 自行編譯的 GPU kernel | 1 支,以符號表確認目標實體化存在 |
| 最終修改檔案數 | 9 個,約 520 KB,全部以差異檔記錄在 patches/ |
| 驗證方式 | 功能驗收五項全過(對數機率有限值、26K 針取回、工具呼叫、繁中長短文零簡體字、思考模式)。Engram 的 CPU 解量化與 GPU kernel 逐位元對照,兩種切片皆相同。效能與方法學見第五、六節與 RESULTS.md |
| 回退路徑 | 拉回官方映像、不掛載 files/ 下任何檔案,即回到未修改的 vLLM 0.30.0。若要回到可服務狀態,改用既有的 EXL3 配方 |
| 變更後的決策 | 不上正式服務,理由見第七節。此組合保留為「上游還缺什麼」的證據與日後的起點 |
| 耗時 | 移植與驗證約 3 小時,不含兩次載入的等待(約 69 分鐘可服務) |
Day 11 是 DGX Spark 的節點基線。今天的移植在一台已經跑過多個配方的機器上做,Engram 分片直接從本機接進來、驅動版本與配方作者不同,這些都是「機器目前是什麼狀態」沒有被記錄下來的結果。明天從 DGX OS 的初始設定、帳號與 SSH 加固開始,把兩台機器整成一樣的、可以描述的狀態,之後 Day 12 的守護與 Day 19 的指標收集才有一致的對象可以量。
系列文章與程式碼索引:onprem-ops-30days
本日程式碼:ivanusto/ @
前一個系列:Day 27|雙 DGX Spark GB10 合體與分工合作的部署與實測