DeepSeek-V4-Flash on DGX Spark · part 2
[地端 LLM] 把 15 tok/s 的 284B 當每天的 agent 大腦:DeepSeek-V4-Flash 怎麼設才舒服
❯ cat --toc
- 白話版:模型跑得動,不等於每天好用
- 前言
- 讓這顆慢模型用得舒服:server 端 + agent 框架端各要設什麼
- decode 不是固定速度,它隨 context 崩
- --no-mmap:同一顆模型同一塊碟,冷啟從 7 分鐘變 57 秒
- KV disk cache:把想過的話寫進硬碟,下次直接撈
- decode 慢歸慢,當 agent 用其實意外地順
- KV 量化整條死:想優化,結果發現沒東西可優化
- 我追了半小時一個不存在的 watchdog——那個 watchdog 是我的 grep
- 共存的牆:單台 128GB,frontier-LLM 跟媒體生成不能舒服共處
- 收穫
- 最花時間的地方
- 帶得走的排查方法
- 通用原則
- TL;DR
TL;DR
在 GB10 上把 DeepSeek-V4-Flash 跑起來只是入場券。要當每天用的 agent,得做記憶體跟 context 工程:decode 會隨 context 從 15 tok/s 崩到 5(64K 時)、--no-mmap 把冷啟從 7 分鐘砍到 57 秒、KV 量化整條死(CUDA concat 只吃 f16)。但 decode 慢不代表用起來慢:撈一天的日用 log,平均揹著 46K context,48% 的回覆 5–15 秒就回完,因為 ds4 的 KV disk cache 把 5 萬 token 的前綴 87ms 從碟撈回來(冷 prefill 要 150 秒)。實際當 agent 跑是雙峰的——熱的順、冷的才卡。還有一個我追了半小時的「每 60 秒重啟的 watchdog」——後來發現那個 watchdog 是我自己的 grep。

本文重點,一圖看懂
白話版:模型跑得動,不等於每天好用
Part 1 把這顆 284B 的大模型在我的小主機上跑起來了。但「跑得動」跟「每天順手用」中間,還隔著一堆工程。
想像你請了一個記性很好的助理,但他有兩個怪毛病:一,聊得愈久,他回話愈慢(因為他每次開口都要把整段對話重想一遍);二,他每次「上班」要花 7 分鐘暖機。這篇就是在處理這些毛病——怎麼讓他暖機快一點、怎麼讓他把想過的話記在硬碟上下次直接用、還有我怎麼蠢到去追一個根本不存在的問題。把一個工具從「能跑」調到「每天靠它幹活」,這些細節才是真正花時間的地方。
前言
一顆模型的 benchmark 數字,跟它「當每天的腦」的體感,是兩件事。前者是空機跑一個 prompt 量 tok/s;後者是它陪你跑了一整天、context 養肥、要跟生圖搶記憶體之後,還順不順手。
這是 DeepSeek-V4-Flash on DGX Spark 系列 Part 2。Part 1 證明它跑得動、15.6 tok/s、工具呼叫正常。這篇是把它接成 daily agent 之後,撞到的每一面記憶體跟 context 的牆——還有為了繞過它們,server 跟 agent 兩邊各要設什麼。
讓這顆慢模型用得舒服:server 端 + agent 框架端各要設什麼
先把結論給你。要把一顆 15 tok/s 的大模型當每天的腦、而且用得舒服,準備分兩半——ds4 server 端,跟你的 agent 框架端。我今天才因為漏設其中一個,光羽當場炸給我看。
ds4 server 端:
--ctx 131072,不要開到 256K——decode 一樣,但 TTFT 變 3 倍(下面有數字)。--kv-disk-dir一定要開——長對話不每次重算 prefill 的關鍵,也是這顆當 agent 意外好用的根本原因。--power 100——預設 75 直接丟 25% 效能。--no-mmap換 57 秒快冷啟,但記憶體 headroom 變緊;要跟生圖之類的東西共存就留 mmap。- 單用戶就
--max-batch-size 1,不用 batching。
agent 框架端(這半最容易漏):
- 告訴框架 server 真實的 context 上限(ds4 是 131072)。沒設、框架自己 fallback 一個更大的預設值 → context 養過頭 → server 回
400 Prompt too long→ 整個 session 卡死。我今天就是漏了這個,光羽每則訊息一進去就炸。 - 壓縮要在撞上限前觸發,而且用一個 off-box 的小模型摘要(我用 Gemini Flash)。別讓 ds4 自己摘要——它單 worker,又慢又會 timeout。
- 單發工具輸出要設上限。一個 terminal 指令吐 50K 字(約 29K token)可以一口氣把 context 撐爆——我量到的一次 overflow 就是這樣來的。
- client 端用
model=deepseek-chat走 non-thinking,繞過一個 thinking 會吞 DSML 的 bug,日常也更直接。
下面每一條設定背後,都是一個我踩過的坑。
decode 不是固定速度,它隨 context 崩
第一個反直覺:decode 速度不是常數,它隨實際 context 長度往下掉。
| 實際送進去的 prompt | TTFT(到首 token) | decode tok/s |
|---|---|---|
| 64 token | 1.9s | 14.4 |
| 4K token | 18.4s | 11.1 |
| 16K token | 73.2s | 9.2 |
| 64K token | 426.7s(7 分鐘!) | 5.2 |
短對話 14.4 tok/s,填到 64K 掉到 5.2——只剩 1/3。即使 DSv4 有壓縮 KV,每個 token 的 attention 成本還是隨 context 增長。網路上有人說「DSv4 的 TPS 不管多長都 ~14」,我實測證實那是錯的。
更要命的是 TTFT。64K 的 prompt 光 prefill 就要 7 分鐘。短對話 1.9 秒無感,但只要 context 養肥,每一輪你都在等。
還有一個容易誤會的點:這跟 -c 設定值無關。我空機跑了乾淨對照(warmup 丟棄、3 runs),-c 64K 跟 -c 256K 在同樣短 prompt 下 decode 都是 14.3。256K 真正的代價在 TTFT——短 prompt 首 token 1.36s @256K vs 0.41s @64K,慢 3 倍。256K 不會讓它想得快,只會讓它每句話多等一秒才開口。 所以 daily 我設 128K。
--no-mmap:同一顆模型同一塊碟,冷啟從 7 分鐘變 57 秒
那個 7 分鐘冷啟一直讓我覺得不對勁。80 GB 的檔案,NVMe 的循序讀取速度怎麼算都不該要 7 分鐘。
真相是 mmap。在 -ngl 99 把 75 GB 填進 CUDA buffer 時,mmap 是 page-by-page 隨機 faulting,實測只有 ~200 MB/s。加上 --no-mmap 改成循序讀取,~1.4 GB/s:
./ds4-server --cuda -m ./ds4flash.gguf --ctx 131072 --no-mmap ...
冷啟從 ~7 分鐘降到 57 秒,6–7 倍。同一顆模型、同一塊碟,只差一個 flag。
但這裡有個取捨:--no-mmap 讓那 80 GB 變成硬 CUDA buffer(不是可回收的 page cache)。跟 ComfyUI 之類的東西共存時,可分配的記憶體 headroom 會變緊。便宜不是白拿的。
KV disk cache:把想過的話寫進硬碟,下次直接撈
ds4 有個我越用越喜歡的東西:KV disk cache。起 server 帶上:
./ds4-server --cuda -m ./ds4flash.gguf \
--kv-disk-dir ./ds4-kv-cache --kv-disk-space-mb 32768 ...
它會把算過的 prefix KV 用 prefix-hash 當 key 寫進 NVMe。我實測過命中效果——同一個 4011 token 的 prompt 送兩次:
- 第一次(冷):4011 token 全程 prefill,11.4 秒。
- 第二次(暖):命中 disk 上的 checkpoint,
kv cache hit tokens=2048 load=47.5ms,只 prefill 剩下的 1963 token,6.5 秒。
省了一半 prefill。載入那 2048 token 的 KV 只花 47 ms,比重算(~6 秒)快 100 倍。而且它支援 cross-quant 重用——換量化也能撈舊的 KV。
這就是為什麼長對話雖然 decode 掉到 5–9 tok/s,但重連的 TTFT 沒有每次都爆 7 分鐘:前綴從碟上撈回來,只有新增的尾巴要重算。它把想過的話寫進硬碟。下次你問同樣的開頭,它不用再想一遍。
decode 慢歸慢,當 agent 用其實意外地順
上面那張 decode 崩到 5 的表,看起來像在說「這顆不能互動用」。我本來也這樣以為。結果接成光羽的大腦跑了一整天,翻 log 出來的手感完全是另一回事——decode 是慢,但實際用起來意外地順。
關鍵是那個越用越愛的 disk KV。撈一段日用 log:155 次呼叫,平均每次揹著 46,507 token 的 context(對話養得很肥,重度 agentic)。按理 46K 冷 prefill 要 ~130 秒,每句話都該等兩分鐘。實際——48% 在 5–15 秒內就回完了。
差別就在命中:那 15 次命中平均把 5 萬 token 的前綴 87 ms 撈回來,冷 prefill 同尺寸要 150 秒,快 1700 倍。所以每一輪那 46K 歷史幾乎都直接從碟撈,真正要算的只有新增那截尾巴加新生成的輸出。decode 還是 9–12 tok/s 沒變快,但它只咬在新 token 上——你不用每輪重付整段 context 的帳。這就是硬碟 KV 香的地方。
所以它其實是兩種速度:
- 熱的(cache 命中):5–15 秒,順。
- 冷的(壓縮後重建、重啟第一則、換全新話題):60–160 秒,卡。「ds4 好慢」的抱怨全來自這條。
headline 那個 15 tok/s 會騙人,就是因為它只講了冷的那半。當 agent 用,你大部分時間在熱的那條。
工具也順帶看了:108 次工具完成,框架層幾乎沒失手。那 37 筆「失敗」拆開全是指令層的——300 秒 timeout、模型自己寫的 Playwright script 噴錯、被我 ctrl-C 砍掉——就是 agent 在那邊試錯,不是工具壞掉。
KV 量化整條死:想優化,結果發現沒東西可優化
既然 context 是瓶頸,我想說那把 KV 量化壓一下吧。結果整條路死掉:
ggml-cuda/concat.cu:244: ggml_cuda_op_concat: unsupported type
-ctk q8_0 崩、-ctv q4_0 也崩,同一面牆——這個 DSv4 llama.cpp fork 的 CUDA concat kernel 不吃量化的 KV 型別,q8_0/q4_0 一律 abort(這是 fork 的 kernel 限制,不是 DSv4 模型本身的問題)。模型載得起來,第一次 inference 就死。
好笑的是,就算能量化也省不到。DSv4 的壓縮 KV 本來就超小:64K context 的 f16 KV 只有 1.7 GB。我想幫一顆已經很省 KV 的模型再省一點,結果它連量化的 KV 都不讓我 concat。這是「想優化才發現沒東西可優化」的好笑收尾。
我追了半小時一個不存在的 watchdog——那個 watchdog 是我的 grep
這段是誠實系列的招牌。
把 daily 設成 --no-mmap 之後,有次 context 養到 56K,一個大 prefill 的工作記憶體頂爆,global_oom 把 server 殺了(dmesg total-vm:339GB)。我重啟,然後注意到一個詭異現象:每隔大約 60 秒,好像有個東西用 --no-mmap 把 server 重開一次,跟我作對。
我開始查。cron?沒有。systemd timer?沒有。launchd?沒有。找了半小時,越找越毛——到底是哪來的 watchdog?
真相讓我想撞牆:我一直用 pgrep -f llama-server 在確認 server 狀態,而那個 pattern 把我自己的診斷 shell 也比中了(指令字串裡含 "llama-server")。那個假 proc 還顯示 "--no-mmap"——因為那是我 grep pattern 裡的字。更慘的是我一度 pkill -9 -f,還把自己的 ssh session 殺掉。
我花了半小時追一個不存在的 watchdog——那個 watchdog 是我自己的 grep。 教訓:檢查用 pgrep -xc(exact comm,不比對整條 command line)、殺用 pkill -x。這個坑我這個系列踩了不只一次。
共存的牆:單台 128GB,frontier-LLM 跟媒體生成不能舒服共處
最後一個工程現實。DSv4 的 80 GB 權重是固定的,加上 context 養肥後的 prefill checkpoints(~14 GB),avail 很快剩個位數 GB。這時候你要叫 ComfyUI 生張圖,它沒空間 stage 模型,同一張圖會從 17 秒暴增到 265 秒——反覆 stage/unstage 抖動。
我試過各種 flag(--disable-pinned-memory 確實有幫上,那是另一個故事),但結論是:單台 128 GB 上,frontier-LLM + 媒體生成沒辦法舒服共存。 你要嘛時間分家(用 --no-mmap 的 57 秒快重啟,要生圖時先停 LLM),要嘛空間分家——把媒體生成整個搬到另一台機器。
我最後選了空間分家:生圖生影片移到一台 RTX 5090,GB10 整台專供 DSv4。共存問題從此根治,不用再每次切換。
收穫
最花時間的地方
那個不存在的 watchdog,半小時。不是因為問題難,是因為我從一開始就問錯問題——「是什麼在重啟 server」,而正確的問題是「我怎麼確認真的有東西在重啟」。pgrep -f 的 false positive 把我自己的診斷工具變成了我在追的鬼。
帶得走的排查方法
兩個。一,pgrep -f / pkill -f 在你自己的 shell 指令含目標字串時會自己比中自己——診斷用 -x(exact comm)。二,OOM「殺別的程式」時別假設「記憶體擠」,先查 dmesg 看真兇(我這系列另一次是 ComfyUI 預設 pin 了 112 GB host memory,模型是無辜的)。
通用原則
context shift 管 token 數(邏輯層),OOM killer 管實體 RAM(物理層),兩個不同層。 清 KV token 救不了實體記憶體尖峰。「不是會自動壓縮清舊的嗎?」——會,但那管的是 context window 的 token 預算,跟 prefill 瞬間的實體記憶體尖峰是兩回事。
TL;DR
- decode 隨 context 崩:14.4(短)→ 5.2(@64K),allocated context 不影響、實際長度才影響。
--no-mmap冷啟 7 分鐘 → 57 秒,代價是記憶體 headroom 變緊。- KV disk cache 命中省一半 prefill(prefix-hash + NVMe + cross-quant),長對話重連不再爆 TTFT。
- KV 量化整條死(CUDA concat 只吃 f16),但 DSv4 KV 本來就小,沒差。
- agent 框架端三件最容易漏:告訴它 server 真實 context 上限(沒設會炸
400 Prompt too long)、壓縮用 off-box 小模型在撞上限前觸發、單發工具輸出設上限。 - 單台 128 GB 跑 frontier-LLM + 媒體生成不能舒服共存——空間分家(媒體搬另一台)是乾淨解。
Also in this series: Part 1 — 在 128GB 小盒子上跑 284B,然後錯怪了 2-bit · Part 3 — 權重就是正義:284B 砍到 2-bit 還是強
常見問題
- DeepSeek-V4-Flash 的 decode 速度會隨 context 變慢嗎?
- 會,而且很明顯。短對話 ~15 tok/s,填到 16K 掉到 ~9,64K 時掉到 ~5。即使 DSv4 有壓縮 KV,每個 token 的 attention 成本還是隨實際 context 長度增加。allocated context(-c 設定值)不影響 decode,真正影響的是『實際送進去多長』。
- 為什麼 ds4/llama.cpp 冷啟要 7 分鐘?怎麼修?
- 因為 mmap 在填 75 GB CUDA buffer 時是 page-by-page 隨機 faulting,大約 200 MB/s。加 --no-mmap 改成循序讀取 ~1.4 GB/s,冷啟從 7 分鐘降到 57 秒。代價是那 80 GB 變成硬 CUDA buffer(不可回收的 page cache),跟其他程式共存時記憶體 headroom 變緊。
- DeepSeek-V4-Flash 的 KV cache 可以量化嗎?
- 不行。-ctk/-ctv 設 q8_0 或 q4_0 都會崩,死在這個 DSv4 fork 的 CUDA concat kernel(不吃量化型別)。但這沒差——DSv4 的壓縮 KV 本來就超小(64K context 的 f16 KV 只有 1.7 GB),量化也省不到。ds4 引擎則提供 KV disk cache,把算過的 prefix 寫進 NVMe 重用。
- ds4 的 KV disk cache 真的有用嗎?
- 有。同一個 4011 token 的 prompt 送兩次:第一次冷 prefill 11.4 秒,第二次命中 disk 上的 prefix checkpoint(載入只花 47 ms),只需 prefill 剩下的部分,降到 6.5 秒。它用 prefix-hash 當 key,寫進真 NVMe,還支援 cross-quant 重用。這就是為什麼長對話雖然 decode 慢,但重連的 TTFT 沒有每次都爆炸。
- decode 只有 5–12 tok/s,DeepSeek-V4-Flash 當 agent 不會很卡嗎?
- 比想像中好,因為它是雙峰的。撈一天的日用 log:155 次 LLM 呼叫平均揹著 46K token 的 context,但 48% 在 5–15 秒內回完——因為 ds4 的 disk KV cache 把大前綴從碟載回來只要 ~87 ms,每輪只重算新增的尾巴。熱路徑(cache 命中)5–15 秒、冷路徑(重啟/壓縮後/換話題)才 60–160 秒。當 agent 用,大部分時間在快的那條。