DGX Spark · part 43
[DGX Spark] 1770 億參數 + 262K context 一起開:Spark 上不用二選一
❯ cat --toc
TL;DR
單台 DGX Spark 跑 Qwen3.8-Flash-Next(176.94B),unsloth UD-Q4_K_XL 約 104 GiB,原生 262,144 context 全開,不用在量化階和 context 之間二選一。載入 4 分 40 秒、常駐約 104 GB。改檔案類的工作配 ngram 投機解碼 76.65 tok/s,寫散文 20.51,18,092 token 的 prefill 是 366.19。關鍵設定是把那張 51B 的 n-gram 查表釘在 CPU 上,其餘全進 GPU。

前言
行李箱塞不下的時候,你會開始算:帶厚外套就不能帶鞋,兩個都要就得換一個更大的箱子。
上個禮拜我把同一顆模型放到三張改裝 2080 Ti 上(合計 66 GB VRAM),整晚都在算那題。想跑品質好一階的 UD-Q4_K_XL?104 GiB 塞不進去,只能上 36 層、十二層掉到 CPU,吐字從 25 掉到 16.02 tok/s。想要原生 262,144 context?層數得從 48 退到 40,滿載之後 decode 剩 4.05。每一格都是拿一樣東西換另一樣。細節在那篇。
這篇換一台 121 GB 統一記憶體的 DGX Spark。104 GiB 的權重直接放、262,144 直接開,沒有那一格要算。
Part 42 量過這台的頻寬天花板,這篇看它扛不扛得起一顆 1770 億參數的模型。
記憶體怎麼分配:51B 不用待在快的那一側
先看數字對不對得起來。權重 104 GiB、統一記憶體 121 GB,中間只剩十幾 GB 要塞下 KV cache——聽起來很緊。
實際上沒那麼緊,因為那 176.94B 裡有 51B 是一張 n-gram 查表(模型把常見的兩三個 token 組合記成一筆,用的時候查出來就好)。它不是一張要整片拿去相乘的權重矩陣,每個 token 只查出稀疏的幾列,所以不需要待在快的那一側。
把它釘在 CPU、從 NVMe 分頁進來,GPU 那側就鬆了:
| 去處 | 內容 |
|---|---|
| CPU + NVMe(mmap) | per_layer_token_embd,那張 51B 的查表 |
| GPU | 其餘 48 層權重 |
| KV cache(dense K/V) | 262,144 token 約 6 GiB |

看這張表的第三列:262K 的 dense K/V 只要約 6 GiB。 這顆 48 層裡只有 12 層是全注意力,其餘是線性注意力(狀態大小固定、不隨 context 長),所以 K/V 每個 token 只有約 24 KiB。
⚠️ 但那不是全部。在同一個 commit 裡,那 12 層每層還多一份 QSA indexer 的快取,每個 token 再多約 9 KiB。兩個加起來約 33 KiB/token,262K 大概 8.25 GiB。長 context 在這個架構上還是便宜,只是別把 6 GiB 當成全部的開銷。
直接抄這組
build/bin/llama-server \
-m Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf \
--alias qwen38-flash-next \
-lm mmap -ot per_layer_token_embd=CPU \
-ngl 999 -c 262144 -np 1 \
--spec-type ngram-mod \
--temp 1.0 --top-p 0.95 --top-k 20 \
--host 127.0.0.1 --port 8199
載入約 4 分 40 秒,穩態常駐約 104 GB。
四個旗標值得單獨講:
-ot per_layer_token_embd=CPU 把那張查表釘在 CPU。-lm mmap 讓它從 NVMe 分頁進來,不是整個讀進記憶體。這兩個是上面那張表能成立的原因。
-ngl 999 其餘全上 GPU。這顆 48 層,寫 999 就是全部。
-np 1 要寫死。 同時開多個請求會撞 qwen4exp.cpp 的 indexer cache assert,直接 abort。
build 有一個預設值不會報錯,但會讓輸出整個壞掉
llama.cpp 是透過 PR #27742 加進 qwen4exp 支援的。這個 PR 在 2026-08-27 合併,我量的時候還開著,所以下面用的是合併前的 6c5afc86a:
git clone https://github.com/ggml-org/llama.cpp ~/llamacpp-qwen4exp
cd ~/llamacpp-qwen4exp
git fetch origin pull/27742/head:pr27742 && git checkout pr27742
cmake -B build -DGGML_CUDA=ON \
-DCMAKE_CUDA_ARCHITECTURES=121 \
-DCMAKE_CUDA_COMPILER=/usr/local/cuda-13.0/bin/nvcc \
-DGGML_NATIVE=OFF -DLLAMA_CURL=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build --target llama-server -j 16
⚠️ -DCMAKE_CUDA_COMPILER 那行不能省。 Spark 上 /usr/local/cuda 走 alternatives、預設指向 13.2,而 PR 討論串有人做過控制變因的 A/B(同原始碼、同旗標、同 GGUF,只換這個參數):用 13.2 編出來的 binary 不報錯、不 assert、server 正常啟動,但輸出是亂碼。同一顆 binary 跑別的架構完全正常,所以一開始很難懷疑是 binary 有問題。
-DGGML_NATIVE=OFF 也是必帶的,否則 ggml-cpu 偵測到 ARM 的時候,會在 Grace 上給出 gcc 不吃的旗標。
編完先問一句再說。 因為失敗是安靜的,我用手上已有的小檔(UD-IQ1_S,68 G)先問 The capital of France is:
" Paris. Given a list of countries and their capitals, answer the question..."
先問這一句,省下的是四個小時——要是下載完 104 GB 才發現 binary 在吐垃圾,那四小時就白花了。
四個數字,因為「一個數字」在這裡沒有意義
開了 ngram 投機解碼之後,同一台機器的速度會因為你在做什麼而差三倍多。先看這張表前兩列的差距:
| 工作 | decode t/s |
|---|---|
| 寫散文(冷) | 20.51 |
| 改檔案(冷) | 76.65 |
| 改檔案(重複第 2 / 3 次) | 88.02 / 78.74 |
| prefill(18,092 token) | 366.19 |
ngram 投機解碼不用另外養一顆小模型,它從你貼進去的 context 裡找重複的片段來草擬。你丟一份檔案要它改一行,輸出幾乎全都已經在輸入裡;你叫它寫散文,沒東西可抄,每個 token 都得老實生成。
對外要引用的是冷啟動的 76.65,不是重複之後的 88——重複之所以快,是因為 ngram 認得你上一次的答案,那不是這個設定的速度。
聰不聰明:UD-Q4_K_XL 在我的七題拿 6/7
我另外設計了七題想分辨量化階的退化:解釋投機解碼、多步年齡推理、寫一個 merge_intervals、多步算術配精確輸出格式、三重指令限制、40 到 60 行的程式、嚴格 JSON。
UD-Q4_K_XL 拿 6/7。唯一失敗的那題是程式長度——程式碼通過 py_compile、通過我寫的所有 assert,只是寫了 38 行,而我的判準要求 40 到 60 行。 那是我的尺太挑,不是模型不會。
⚠️ 這七題是在另一台機器(三張 2080 Ti)上跑同一份 UD-Q4_K_XL 權重量的,Spark 這邊沒有重跑。同樣的權重、同樣的判準,但不是同一台機器的數字,引用時要分清楚。
那台老卡跑得一樣快,只是每一格都得挑
回頭對前言那三張 2080 Ti:
| DGX Spark | 3× 2080 Ti | |
|---|---|---|
| 跑得動的量化 | UD-Q4_K_XL(104 G) | UD-IQ4_XS(88 G) |
| 改檔案(冷) | 76.65 | 77.56 |
| 262K 的代價 | 沒有 | decode 掉到 4.05 |

看這張表的第二列:吐字速度幾乎打平,76.65 對 77.56。
換句話說,這台十倍價錢的機器沒有買到速度。它買到的是第三列——2080 Ti 那邊每開一格 context、每上一階量化,都要從別的地方扣回來;這邊不用。
那個差別在你只跑一種工作的時候看不出來,在你想同時要「好一點的量化」跟「整份 codebase 的 context」的時候才會出現。
進階:三段量測紀錄
不讀這節不影響使用,上面那組指令抄走就能跑。
下載卡在 74 KB/s,關掉 Xet 之後 88 分鐘跑完
遇到什麼問題:hf download 抓 104 GiB,log 預估只剩約 74 KB/s。
原本打算怎麼解:以為是家裡的網路或 HF 那邊在限速,想等離峰再抓。
所以做了什麼:先拿 HF CDN 直接做 range request 對照,量到約 4.30 MB/s。所以不是網路。
結果:問題在 huggingface_hub 1.28.0 的 Xet 傳輸層——它會做 adaptive concurrency,實測把並行從十條一路降到一條。保留已下載的 partial,加 HF_HUB_DISABLE_XET=1 重啟,三個大 shard 改走一般 HTTP 並行,1 小時 28 分 26 秒完成。
HF_HUB_DISABLE_XET=1 hf download \
unsloth/Qwen3.8-Flash-Next-GGUF --include "*UD-Q4_K_XL*" \
--local-dir ~/models/qwen38-flashnext-gguf/UD-Q4_K_XL
回頭看哪裡想錯了:我把「慢」預設歸給網路,因為那是最常見的原因。而分辨這兩者只要一發 range request——先跑一次繞過那個傳輸層的下載,三十秒就知道要往哪查。
載入的時候整台機器從網路上消失三分鐘
遇到什麼問題:跑 262K 的正式量測,載入到一半 SSH 斷了。
原本打算怎麼解:以為 OOM 把機器打掛,準備去看 dmesg 撿屍。
所以做了什麼:等。約三分鐘後 Tailscale 自己回來。
結果:主機沒有 reboot、dmesg 沒有任何 OOM kill 紀錄、server 的 health check 正常、模型也順利載入。整個載入 4 分 40 秒,那三分鐘落在中間。
回頭看哪裡想錯了:我把「連不上」讀成「掛了」。實際上是載入把記憶體與 I/O 吃滿的那段時間,網路那邊搶不到資源。斷線是症狀,不是死因——這台只有一個記憶體池,載入 104 GB 的時候它是真的很忙。下次先等,不要急著判死。
三次同樣的請求,第三次的輸出不一樣
遇到什麼問題:同一個改檔請求送三次,前兩次 SHA256 完全相同,第三次不同。投機解碼理論上不該改變輸出。
原本打算怎麼解:懷疑投機解碼在這個架構上不精確,準備去追 llama.cpp 那條路徑。
所以做了什麼:先看 diff。只有一處:
--- copy-1.txt
+++ copy-3.txt
@@ -1,4 +1,3 @@
-```python
from __future__ import annotations
@@ -129,4 +128,3 @@
return # skip other tensors
-```
結果:程式本體三次完全相同,差的只是第三次沒把 Markdown 的 code fence 包上去。而這台的 server 是照上面那組指令起的,帶著 --temp 1.0,request 本身沒有覆寫 temperature——那是取樣的隨機性。
回頭看哪裡想錯了:投機解碼保證的是「分佈不變」,不是「隨機取樣之下也逐字相同」。我把兩件事混在一起,差點去追一個不存在的 bug。要真的驗它精不精確,得把 temperature 壓到 0 或固定 sampler seed——這台我還沒補測。(同一顆模型我在 2080 Ti 那台用 temperature=0 跑過三次,SHA256 完全相同,那組才是有效的一致性證據。)
環境
DGX Spark(GB10、121 GB 統一記憶體、sm_121a、aarch64、驅動 580.159.03)。llama.cpp 走 PR #27742 的 6c5afc86a,用 CUDA 13.0.88 編、CMAKE_CUDA_ARCHITECTURES=121(CMake 自己會換成 121a)。模型是 unsloth/Qwen3.8-Flash-Next-GGUF 的 UD-Q4_K_XL,四個 shard 合計約 104 GiB。
⚠️ configure 會印出兩個版本號:The CUDA compiler identification is NVIDIA 13.0.88 是實際用來編的,Found CUDAToolkit ... 13.2.78 是 CMake 找到的 headers 路徑。看前者。
prefill 那格的數字要講清楚:API 回報 18,134 個 prompt token,其中 42 個命中前綴快取,實際評估的是 18,092 個——366.19 tok/s 是對那 18,092 個算的。
還有一件小事:第一次量 prefill 我挑的 prompt 只有 5,680 個 token,太短——固定開銷會蓋過一切,量不出 prefill。換成 18,134 個 token 重跑一次才算數。
常見問題
- DGX Spark 跑得動 1770 億參數的模型嗎?
- 跑得動。Qwen3.8-Flash-Next 的 GGUF 是 176.94B,用 unsloth 的 UD-Q4_K_XL(四個 shard、約 104 GiB),在單台 DGX Spark 上原生 262,144 context 全開,載入約 4 分 40 秒、穩態常駐約 104 GB。實測寫散文 20.51 tok/s,改檔案類的工作配 ngram 投機解碼是 76.65 tok/s。
- 121 GB 統一記憶體要怎麼分配才放得下 104 GiB 的權重加 262K context?
- 關鍵是那張 51B 的 n-gram 查表不用進快的那一側。用 `-ot per_layer_token_embd=CPU` 把它釘在 CPU、配 `-lm mmap` 從 NVMe 分頁進來,其餘 `-ngl 999` 全上。這樣 262,144 的 KV 也放得下——這顆 48 層裡只有 12 層有全注意力,dense K/V 每 token 約 24 KiB、262K 約 6 GiB;再算上 QSA indexer 的快取,會隨 context 增加的用量合計約 8.25 GiB。
- Spark 上跑這顆需要什麼特別的設定?
- 三件事:llama.cpp 的 `qwen4exp` 支援走 PR #27742(2026-08-27 合併,本文用的是合併前的 `6c5afc86a`);build 要明確指定 CUDA toolkit 13.0 的 nvcc,不能用 `/usr/local/cuda` 的預設 13.2;`-np 1` 要寫死,同時開多個請求會撞 indexer cache 的 assert。
接著讀
- 2026-06-01[Benchmark] NVFP4 W4A4 在 DGX Spark 上超車 FP8:拔掉 enforce-eager,MoE 從 23 衝到 67 tok/s
GB10 上 NVFP4 W4A4 拔掉 --enforce-eager 後從 23 衝到 67 tok/s,贏 FP8 29% 還省 16GB。Part 32 說 cudagraph 沒用——那只對 dense,MoE 完全相反。
- 2026-05-01[vLLM] DGX Spark 跑 Nemotron 3 Nano NVFP4:74.75 tok/s,比公開值快 11.5%
十天前我說 NVFP4 在 DGX Spark 上是個坑、FP8 比較快。今天同一台機器跑 Nemotron 3 Nano W4A16 飆到 74.75 tok/s,連我自己之前的 FP8 hack 紀錄一起踩過去。這篇講 4 層 patch、quant variant 怎麼選、跟記憶體頻寬天花板的算法。
- 2026-04-28[llm-compressor] 自量化 abliterated 35B FP8 on DGX Spark:4 次 OOM、3 個 prefix bug、最終 51 tok/s
把 huihui-ai 的 Qwen3.6-35B-A3B abliterated BF16 量化成 FP8,部署到 DGX Spark GB10。從 4 次 OOM 到 1.68× over BF16 的完整旅程:UMA 物理上限、save_pretrained 的 50GB shard 陷阱、語言模型 prefix bug、MTP speculative decoding,以及為什麼第一個成功的版本根本沒做 FP8 cast。
- 2026-04-13[Benchmark] Gemma 4 全家桶 on DGX Spark — 哪個版本適合你?
Gemma 4 E2B / E4B / 26B MoE / 31B Dense 在 DGX Spark、RTX 5090、MacBook Pro 上的完整對照表。一張表看完速度、記憶體、量化格式。附選擇建議。
不想錯過新文章?
訂閱我確保不漏接!
隨時一鍵退訂。