改裝 2080 Ti 22G · part 17
[Benchmark] 1770 億參數塞進三張二手 2080 Ti:改檔案快 3.5 倍,寫散文一點都沒快
❯ cat --toc
TL;DR
三張改裝 2080 Ti(66 GB VRAM)跑 Qwen3.8-Flash-Next,176.94B 全部塞得下,32K 那格 48 層零卸載。128K context 下用 46 層、decode 23.14 tok/s;開 --spec-type ngram-mod 之後,改檔案類的工作衝到 77.56(3.51 倍,輸出位元完全不變),寫散文則是 +0.17%,等於沒有。原生 262,144 context 真的載得起來、255,125 token 的 needle 三針全過,但首輪 prefill 要等 66.68 分鐘。
前言
搬家的時候最重的那幾箱,常常是你最少打開的。
這一顆 Qwen3.8-Flash-Next 就是那種行李:官方標 1770 億參數,我手上的 GGUF 載入時印 176.94B——而其中 51B 是一張查表,每個 token 只翻一次,翻完就闔上。
Part 3 那時候我在一張卡上跟 27B 的 KV cache 打架,Part 14 開始用兩張卡跑 tensor parallel。這篇換三張卡、換一顆大一個量級的模型,問題也換了:它塞得下嗎,塞下去之後日常該怎麼設。
176.94B 塞進 66 GB,因為其中 51B 是查表不是矩陣
VRAM 只有 66 GB,權重印出來 176.94B,中間差了兩倍多。這個差不是靠壓縮補的,是靠架構。
GGUF 的表頭直接寫著,不用猜:
qwen4exp.block_count = 48
qwen4exp.expert_count = 512 expert_used_count = 10
qwen4exp.context_length = 262144
qwen4exp.ple.layers = 1
qwen4exp.ple.ngram_size = 3 heads_per_ngram = 8
兩件事讓它縮下來。第一件是稀疏:512 顆專家,每個 token 只點名 10 顆 routed 加 1 顆 shared,真正參與運算的大約 6B。這個大家比較熟。
第二件比較少見。那 51B 是一張 n-gram 查表(模型會把「連續兩三個 token 的組合」直接記成一筆,用的時候查出來就好,不用每次重新算)。重點是它不是一張要整片拿去相乘的權重矩陣:每個 token 查出來的是稀疏的幾列,那幾列後續還是會被投影、加權,再混回主幹,但吃到運算的是那幾列,不是那 51B。而 ple.layers = 1 那行是關鍵:48 層裡只有一層掛著這張表,所以「每個 token 只查一次」不是修辭。

順帶把三個到處流傳的參數量講清楚。官方 model card 寫的是「125B with 6B activated, plus 51B n-gram embedding and 4B MTP」,加起來 180B;我的 GGUF 印 176.94B,少的 4B 是轉檔時被丟掉的 MTP 頭(我掃過 1224 個張量,nextn 相關的是 0 個)。三個數字都對,只是在數不同的東西。
直接抄這組:128K context、23.14 tok/s
如果只是要直接拿來跑,我會用這組:
llama-server -m Qwen3.8-Flash-Next-UD-IQ4_XS-00001-of-00003.gguf \
--split-mode layer -np 1 --no-mmap -fa on -t 24 \
-ngl 46 -c 131072 -ctk q8_0 -ctv q8_0 \
--spec-type ngram-mod
三個旗標值得單獨講:
不需要 -ot。 我整個過程都以為必須把專家層趕去 CPU,實測三個操作點全部零 tensor override。32K 那格甚至 48 層全上 VRAM。
-np 1 要寫死。 預設會開 4 個 slot,prefill 直接掉一半。
KV 用 q8_0。 f16 在 128K 只能撐到 -ngl 45(22.66 tok/s),換成 q8_0 省下的量剛好夠多塞一層上去,變成 -ngl 46、23.14 tok/s。換 q8_0 不只是讓 VRAM 數字變小,而是真的能多放一層進去。
改檔案快 3.5 倍,寫散文一點都沒快
--spec-type ngram-mod 是這次最有效的加速手段,但不是每種工作都吃得到。同一台機器、同一份權重、同一組旗標,只換工作內容:
| 工作 | 關投機 | 開 ngram-mod | 倍率 |
|---|---|---|---|
| 寫散文(冷) | 22.98 | 23.02 | +0.17% |
| 改檔案(冷) | 22.09 | 77.56 | 3.51 倍 |
這表看第二列:同一顆模型,換個工作就差 3.5 倍。
機制很直白。ngram 投機解碼(不用另外養一顆小模型,直接從 context 裡的重複片段猜下一段)是從你貼進去的東西裡草擬候選。你丟一份 130 行的 Python 進去、要它把一個 class 改名,輸出幾乎全都已經在輸入裡,它一次可以草擬幾十個 token 讓大模型驗證,草稿接受率量到 90.3%。你叫它寫散文,沒有東西可抄,每個 token 都得老實生成。

這不會犧牲品質。 投機解碼不會改變最後的輸出——大模型會逐一驗證草擬的 token,不對就丟掉。我把三次的輸出做 SHA256 比對,完全相同,兩份 diff 都是空的。
128K 是甜蜜點,262K 是懸崖
轉折點和懸崖只隔一格:
32K → 128K decode 26.16 → 23.14 −11.5% 換到 4 倍 context
128K → 262K decode 23.14 → 4.05 −82.5% 只換到 2 倍

前半段只掉一成速度就換到四倍 context,很划算;後半段完全不划算。而且 262K 那格的首輪 prefill 要 66.68 分鐘——那不是慢,是不能互動。
那格還是值得跑一次,因為它證明了一件事:官方標示的原生 262,144 context,三張二手卡真的載得起來。 我塞了 255,125 個 token 進去,把三個各不相同的獨特事實埋在大約 10%、50%、90% 的深度,三針全部答對。
⚠️ 但「載得進去」不等於「跑得完」。-ngl 45 配 -ub 512 可以正常啟動、正常回應,然後在餵到第 83,968 個 token 時,CUDA1 還要再分配 1245.72 MiB,接著 OOM、segfault。這個坑 Part 3 在 27B 上踩過一次,換一顆完全不同架構的模型,它又長回來了。
三個量化怎麼挑:更小的那個不會比較快
同一顆 binary、同一組旗標、同樣 8,121 token 的長 prompt,三個量化並排:
| 量化 | 檔案 | 最大 ngl | decode | 最大可用 context |
|---|---|---|---|---|
| UD-IQ1_S | 68 G | 48 | 26.74 | 未確認 |
| UD-IQ4_XS | 88 G | 48 | 25.56 | 262,144(needle 3/3) |
| UD-Q4_K_XL | 104 G | 36 | 16.02 | 131,072(needle 3/3) |
這表看 decode 那一欄:IQ1_S 比 IQ4_XS 只快 4.6%。
1-bit 雖然少用了 20 GB,速度卻沒快多少,因為這兩個量化本來都塞得進 66 GB,48 層也都放得上 VRAM。Part 12 拆過這件事的底層原因——這張卡上量化省的是 VRAM 不是時間——換到 177B 的量級還是一樣成立。
真正掉速的是 Q4_K_XL:104 G 塞不進 66 GB,只能上 36 層,十二層掉到 CPU 上算,decode 剩 16.02。它的 context 上限也只驗到 131,072。
至於聰不聰明,我另外設計了七題想分辨低位元的退化。結果三個量化全部 6/7,一題也分不出來——細節在下面第二幕。所以我選 4-bit,看的是速度、context 上限跟 unsloth 公布的 KLD 曲線,不是因為我實測出它比較聰明。
進階:量測紀錄
不讀這節不影響使用,上面那組指令抄走就能跑。這節是十輪量測裡三個轉折,還有完整數據。
我以為要把專家層趕去 CPU
遇到什麼問題:176.94B 的權重、66 GB 的 VRAM,第一直覺是一定塞不下,得靠 -ot 把 MoE 的專家層丟去 CPU。
原本打算怎麼解:我從第一發指令就掛著 -ot,而且整晚都在調要卸幾層,-ngl 一路壓在 45 附近。心裡的模型是「熱權重塞不進去,所以要挑一部分放 CPU」。
所以做了什麼:後來排取捨曲線時,我讓它從 -ngl 48 往下掃,想找每個 context 下的上限。
結果:32K 那格 -ngl 48 直接過了,三卡合計 62,720 MiB,decode 26.16。48 層是這顆模型的全部層數,也就是零卸載。三個操作點回頭再驗,全部都不需要 -ot。
回頭看哪裡想錯了:我那個「塞不下」的直覺算的是 176.94B,但真正要進 VRAM 的是熱權重——51B 的查表本來就會落到 CPU(那張表是 IQ4_NL,CUDA host buffer 不收),而它佔了將近三分之一。我把「模型有多大」跟「要放進 VRAM 的有多大」當成同一件事,前面幾輪的 -ngl 45 全是白白把三層留在 CPU 上。

順帶一提,我後來還測了「顯式寫 -ot per_layer_token_embd=CPU」跟「靠自動放置」的差別:兩邊的 buffer 行逐字相同,CPU 27465.95 MiB、三張卡 18692.63 / 18686.69 / 18369.99。那張表本來就會自己下去。
能力題那張表,第一版整張是假的
遇到什麼問題:前三題(解釋投機解碼、多步年齡推理、寫一個 merge_intervals)三個量化都過,分不出高下。1-bit 的退化應該要看得見才對。
原本打算怎麼解:加四題難的——多步算術配精確輸出格式、三重指令限制、40 到 60 行的程式、嚴格 JSON。想著位元越低,遵守格式的能力應該越先崩。
所以做了什麼:跑完拿到 IQ1_S 3/7、IQ4_XS 5/7、Q4_K_XL 4/7。漂亮的單調關係,正好證明我的假設。差一點就這樣寫進文章。
結果:我去翻原始 response,看到這個:
Q4 finish=length completion_tokens=512 content_len=0
Q6 finish=length completion_tokens=2048 content_len=0
Q5 (IQ1_S) finish=length completion_tokens=256 content_len=0
Q5 (IQ4_XS) finish=stop completion_tokens=255 content_len=34
finish=length 加上內容為空,意思是模型把整個 token 預算燒在 thinking 區塊裡,可見輸出是空的。那不是答錯,是沒答。 而我的判定程式把「沒答」記成 FAIL。
重跑的時候改三件事:max_tokens 拉到 4096、關思考改用 request 層的 "chat_template_kwargs":{"enable_thinking":false}、判定改成三態——PASS / FAIL / INVALID(不計入分母)。重跑後 21 格全部有效,結果是:
| 量化 | 通過 | 唯一失敗的那題 |
|---|---|---|
| UD-IQ1_S | 6/7 | Q6 |
| UD-IQ4_XS | 6/7 | Q6 |
| UD-Q4_K_XL | 6/7 | Q6 |
而 Q6 那題三個都敗,敗法還一模一樣:程式碼通過 py_compile、通過我寫的所有 assert,只是寫了 38 行,而我的判準要求 40 到 60 行。 那是我的尺太挑,不是模型不會。
回頭看哪裡想錯了:我以為「加難題就能拉開差距」,錯的不是題目難度,是我沒有讓判準去分辨「答錯」和「沒答」。開跑前我還特地在工單裡寫了「判準要事先凍結,不准看完輸出才決定什麼算過」——那條寫對了,但它只擋住「事後改標準」,擋不住「標準本身分不出兩種零分」。
順便得到一個別的測試也用得上的結論:只在 prompt 裡寫 /no_think 不可靠。舊那批照樣產生了 221 個字元的 reasoning_content;改用 request 層的 chat_template_kwargs 之後,21 個 request 的 reasoning 長度全部是 0。
262K 載得進去,跑到 83,968 個 token 就死
遇到什麼問題:262K 那格要找能跑滿的最大 -ngl。
原本打算怎麼解:載得起來就算數,往上加到 OOM 為止,最後一個 READY 的就是答案。
所以做了什麼:-ngl 45 -c 262144 -ub 512 正常啟動、model loaded、正常回應短請求。看起來就是它了。
結果:真的餵長 prompt 進去,跑到第 83,968 個 token 時 CUDA1 還要再分配 1245.72 MiB,接著 OOM、segfault。往下退到 -ngl 40 才真的跑得完那 255,125 個 token。
回頭看哪裡想錯了:我當時只看能不能啟動,卻漏算了 KV cache 會隨 context 變大——剛啟動時它還沒長出來。這件事 Part 3 在 27B 上已經寫過一次,我還是在一顆完全不同架構的模型上照樣踩。所以這不是哪顆模型特有的問題:服務能啟動只代表當下塞得下,不保證 context 長大之後也跑得完。
對照:同一顆模型在 DGX Spark 上
同一顆 Qwen3.8-Flash-Next,換到 GB10(121 GB 統一記憶體),跑更大的 UD-Q4_K_XL:
| 工作 | GX10(Q4_K_XL) | ai-lab(IQ4_XS) |
|---|---|---|
| 寫散文(冷) | 20.51 | 23.02 |
| 改檔案(冷) | 76.65 | 77.56 |
| 改檔案(第 2 / 3 次) | 88.02 / 78.74 | 67.14 / 65.42 |
| prefill | 366.19(18,092 token) | 63.77(255,125 token) |
有 121 GB 統一記憶體之後,就不用在量化階和 context 之間取捨——104 G 的權重直接放、262,144 直接開、載入 4 分 40 秒、常駐約 104 GB。三張 2080 Ti 要跑同一個量化只能上 36 層,掉到 16.02。
但單串流的吐字速度並沒有比較快。改檔案那格幾乎打平(76.65 對 77.56)。
還有兩個異常沒查清楚。一是載入那 4 分半期間,Tailscale 跟 SSH 斷了大約三分鐘(主機沒有 reboot、dmesg 沒有 OOM kill 紀錄,server 最後正常 health)。二是 GX10 上第 3 次改檔的輸出少了 Markdown 的 code fence,所以「三次逐字相同」沒過——而 ai-lab 那邊三次 SHA256 完全相同。投機解碼理論上輸出不該變,這條還沒追。
還有一個沒解釋的:在 128K 這格重複同一個請求是越來越慢(77.56 → 67.14 → 65.42,草稿接受率同步從 90.3% 掉到 79.3%),但在 32K 那格是相反的(82.54 → 101.99 → 123.18)。兩邊都有關投機的對照組,數字是真的,原因我還沒查出來。
完整數據
環境:EPYC 7402(24C/48T)、三張 RTX 2080 Ti 22 GiB、123 GB RAM。llama.cpp 走 PR #27742 的 6c5afc86a,binary SHA256 5963505c…83d1077b,開工前後都驗過。模型是 unsloth/Qwen3.8-Flash-Next-GGUF。
NCCL 每一輪都用 /proc/<pid>/maps 確認實際載入的是自編的 2.31.2——系統那顆 2.22.3 在 CUDA 13 驅動上是 stub,會偽裝成 OOM,這條 Part 16 已經拆過,這次只是照著避開。
| context | ngl | KV | decode t/s | prefill t/s | 三卡 VRAM |
|---|---|---|---|---|---|
| 8,192 | 45 | f16 | 22.62 | — | 58,020 MiB |
| 8,192 | 45 | q8_0 | 22.41 | — | 57,910 MiB |
| 32,768 | 49 | f16 | 啟動 OOM | — | — |
| 32,768 | 48 | f16 | 26.16 | — | 62,720 MiB |
| 32,768 | 48 | q8_0 | 25.71 | — | 62,304 MiB |
| 131,072 | 47 | q8_0 | 啟動 OOM | — | — |
| 131,072 | 46 | q8_0 | 23.14 | — | 61,942 MiB |
| 131,072 | 45 | f16 | 22.66 | — | — |
| 262,144 | 45 | q8_0 | 83,968 token 時 OOM | — | — |
| 262,144 | 40 | q8_0 | 4.05 | 63.77 | 57,044 MiB |
⚠️ prefill 那一欄只有最後一列填得出來。前面那些格子我一開始是用 43 到 81 個 token 的短 prompt 量的,那個長度量不出 prefill——固定開銷佔了壓倒性比重。同一組設定我在短 prompt 上量到過方向完全相反的結論,兩邊都不成立,所以那些數字全部作廢,只留下真的用 8,000 個 token 以上量的。
三量化那組的完整版:
| 量化 | 檔案 | 最大 ngl | decode(中位/全距) | prefill(8,121 token) | 能力 |
|---|---|---|---|---|---|
| UD-IQ1_S | 68 G | 48 | 26.74(26.70-26.76) | 533.08 | 6/7 |
| UD-IQ4_XS | 88 G | 48 | 25.56(25.48-25.78) | 529.02 | 6/7 |
| UD-Q4_K_XL | 104 G | 36 | 16.02(16.01-16.04) | 329.52 | 6/7 |
IQ1_S 的 context 上限沒驗出來:262,144 可以 READY,但 255K 的 needle 探到 247,850 個 token(97%)之後 client 端逾時,所以那格留白,不寫成「支援」。
Q4_K_XL 的 -ngl 37 可以 READY 但長 prompt 會 runtime OOM,跟上面 262K 那格是同一種病。
十輪量測裡有兩批數字被我自己作廢:第一批速度數字量的時候 build 落後上游 11 個 commit,第一批能力分數把「沒答」讀成「答錯」。兩次出錯的都是量測方法,不是模型本身。
真正留下來的那句話很短:這顆模型在這張老卡上,速度取決於你在做什麼,不取決於你挑哪一階量化。
常見問題
- 1770 億參數的模型要多少 VRAM 才跑得動?
- 這顆不用 1770 億份的 VRAM。Qwen3.8-Flash-Next 的 176.94B 裡有 51B 是一張 n-gram 查表——它不是拿整片去相乘的權重矩陣,每個 token 只查出稀疏的幾列;512 個專家每次只點名 10 顆 routed 加 1 顆 shared。實測三張改裝 2080 Ti(合計 66 GB)跑 UD-IQ4_XS,32K context 下 48 層全部放得進 VRAM、decode 26.16 tok/s;128K 用 46 層,decode 23.14 tok/s。
- ngram 投機解碼可以加速多少?
- 看你在做什麼。實測改檔案類的工作(貼一份 130 行的 Python 進去、要它改一個 class 名)從 22.09 衝到 77.56 tok/s,3.51 倍;同一台機器同一組設定,叫它寫一篇散文只有 +0.17%,等於沒有。差別在 ngram 是從 context 的重複裡草擬候選,散文沒東西可抄。
- 量化階選越低會不會比較快?
- 在這張卡上不會。UD-IQ1_S(68 G)比 UD-IQ4_XS(88 G)只快 4.6%,因為兩個都塞得進 66 GB VRAM、48 層全上。真正掉速的是 UD-Q4_K_XL(104 G),塞不下只能上 36 層,十二層掉到 CPU 上算,decode 剩 16.02 tok/s。
接著讀
- 2026-08-21[Benchmark] 換一個檔案、改一個數字,2080 Ti 上的 27B 免費快 15%
Qwen3.8-27B 在 2080 Ti 的免費加速實測:修復版 chat template 讓 KV 快取命中 94.5%,MTP 草稿深度 2 開到 4 讓 decode 從 41.6 到 47.6 tok/s。附完整驗證方法。
- 2026-08-23[Benchmark] 我把 2080 Ti 的 NCCL 修好了,然後發現它沒快多少
Ubuntu 的 libnccl2 跟 CUDA 13 驅動不相容,自編 NCCL 2.31.2 修好之後,prefill 的 PCIe 流量掉了 99%——而速度幾乎沒動。那個看起來很糟的 fallback,其實沒讓你付多少代價。
- 2026-08-23[Benchmark] 讀者問雙卡 AllReduce 吃多少 PCIe:只佔 8%,因為搬得少、跑得勤
有人來信要我在跑雙 2080 Ti tensor parallel 時開 nvidia-smi dmon -s t 看 PCIe 頻寬。實測 AllReduce 吐字時只用掉不到 1%、prefill 約 6.6%,最忙的一格是 8%——瓶頸不是頻寬是延遲。
- 2026-08-22[Benchmark] 兩張改裝 2080 Ti 玩 tensor parallel,Qwen3.8-27B 衝到 59.6 tok/s
兩張改裝 2080 Ti 22G 用 llama.cpp 的 tensor parallel 接上 Qwen3.8-27B,配 MTP 投機解碼從單卡 37.1 tok/s 衝到雙卡 59.632 tok/s,還能開到 262K context。內含現在正式在跑的組態、三個上游踩坑,和雙 5060 Ti 社群數字的對照。
不想錯過新文章?
訂閱我確保不漏接!
隨時一鍵退訂。