DeepSeek-V4-Flash on DGX Spark · part 10
[DeepSeek-V4-Flash] 上次那個白賺的引擎升級,後來 OOM 討債:我為什麼把光羽換回舊版
❯ cat --toc
TL;DR
上一篇換 Entrpi 白賺 decode 14-16→約 20,爽。我就順手把 context 往上開——結果爆了、變超慢。查到底才發現:Entrpi 為了快把權重存第二份 78.71GiB artifacts,剛好把 119GiB 機器填到 README 寫的「~16K context」那條線;我開到 320K、512K = 20-30 倍,沒剩空間,大 prefill 一來就 OOM、兩分鐘重啟。換回單份權重的舊引擎:decode 15.5 慢一點,但留得住 11GiB、不再崩。速度是有代價的——它把留給大 context 的空間吃光了。
白話版:更快的引擎,是拿記憶體換來的
先講給沒在碰這行的朋友聽。上一篇我做了一件很爽的事:同一顆 AI 模型、同一台機器,只換掉底下那支「跑模型的程式」(引擎),速度就快了三分之一。聽起來像白賺。
但那個「快」不是變魔術變出來的。新引擎為了跑得快,開機時把模型的權重重新排一份、專門給 GPU(顯卡)用的格式——問題是,原本那份權重還躺在記憶體裡沒走。等於同一批權重,在記憶體裡存了兩份。
平常沒事,因為大部分時間用不到那麼多空間。但這台機器本來就快滿了,兩份權重一疊,連喘息的空間都沒了。而我偏偏在看它變快、玩得正爽的時候,想讓 agent 記更長的對話,把 context 往上開。一開大,那點剩餘空間瞬間就不夠——大請求一來,整台爆掉、被系統砍掉,再花兩分鐘重啟。你在那兩分鐘裡用它,當然覺得超慢。
說穿了,慢的不是模型、也不是我上次量錯速度——是那份多出來的權重把空間佔滿,我又硬要往上加 context。這篇就是我怎麼撞上這件事、怎麼查、最後為什麼換回慢一點、但留得住空間的舊引擎。
起點:看它變快看得太爽,我就想把 context 開更大
上一篇我很開心,把 ds4 引擎換成 Entrpi,decode 從 14-16 拉到約 20、prefill 大概兩倍,而且模型一個字沒動。光羽(那顆常駐在 DGX Spark 上、當我整套家用 agent 大腦的 DeepSeek-V4-Flash)一下變快,我就起了貪念:既然這麼快,不如把 context 開大一點,讓 agent 記更長的對話、翻更多歷史。
結果它就在那裡爆了、變超慢——有些回合正常,有些卡個兩分鐘。帳面上明明升級了,怎麼反而更慢?升級的東西,回頭討債了。
目標:搞懂它為什麼慢,然後在「快」跟「穩」之間選一個
這篇只有一個目標:把光羽變慢的真因挖出來,然後做一個決定——是留 Entrpi(快、但會出事),還是換回舊引擎(慢一點、但穩)。
過程中我走錯了兩條路(以為是 thinking、以為是 KV),這段繞路探底,剛好成了這篇的推演主軸。真相跟我一開始猜的完全不同。
真相不是「慢」,是龜速到像壞掉
第一個直覺是「DS4 是會思考的模型,是不是 thinking 吐太多字?要不要關掉 think?」——結果錯。thinking 從頭到尾都不是問題。
我下去量,server 自己回報的 decode 是 1.4 tok/s(正常約 20,慢了 14 倍)。但真正的指紋在這裡:GPU 使用率只有 5%、CPU 也閒著,卻只跑得出 1.4 tok/s。它不是在算,是在等——模型的 working set 被換出去了,每吐一個字都要把權重從別的地方一頁一頁搬回來。難怪 /v1/models 秒回、但真的生成就卡到 timeout。

兩條繞遠路:managed KV 跟一個 flag
繞路一:managed KV。 光羽當時開 512K context,Entrpi 啟動 log 自己有一行紅字:using managed KV cache for ctx=524288 ... may degrade performance。看起來就是它——512K 走了會拖慢的 managed KV 路徑。重啟一下、回到 20.4 tok/s。我把 context 從 512K 降到 320K(這個大小走的是不會退化的 native KV),想一勞永逸躲開它。
結果 320K 反而直接 OOM——systemd journal 白紙黑字 killed by the OOM killer。躲掉了會拖慢的病,換來直接崩。
繞路二:一個叫 --warm-weights 的 flag。 我抓到目前的啟動指令裡有這個 flag,直覺是它把整份權重預熱進記憶體、佔太多。讀了 Entrpi 原始碼確認機制(細節在最後的 debug 段),把它移掉——日常請求(31K prefill)確實從崩到穩了。
但故事沒完:移掉 warm-weights 只治好日常,一個更大的 prompt(211K token)還是 OOM。 我還在原地打轉。
一句話點破:「可是舊版不會 OOM 誒」
轉折是 user 丟來的一句話:「可是我舊版本的 ds4 不會 OOM 欸。」
這句直接把我從細節裡拎出來。我回頭比對舊引擎的設定——它也開 --warm-weights、也跑 256K 這種大 context,卻從來不 OOM。 所以會不會出事,不是那個 flag 決定的,是引擎本身留不留得下空間。
順著這條線挖到底:Entrpi 為了把 decode 從 16 拉到 20,開機時把權重 repack 成一份 78.71GiB 的 device artifacts(給 Blackwell 的 CUDA kernel 用的排列方式)。但那份 78.71GiB 是額外多存的一份——原本 80.76GiB 的權重 mmap 還在。等於同一批權重,記憶體裡有兩份。
DGX Spark 標稱 128GB,實際可用約 119GiB。而 Entrpi 的 README 把話講得很白:這 119GiB「夠跑模型 + 16K context 的 KV」——它是照 ~16K context 抓的。 兩份權重疊起來,剛好把整台填到那條線、只剩約 2GiB;舊引擎只有一份,同一台留得住約 11GiB。
啊,原來如此。 Entrpi 那多出來的速度,是拿留給大 context 的空間換的。16K 以內都沒事——可是我偏偏看它變快、爽過了頭,把 context 開到 320K、512K,是那條線的 20-30 倍。那 2GiB 根本不夠塞我硬加上去的 context,一個大 prefill 的尖峰一來就崩;舊引擎的 11GiB 才撐得住。

決定:換回舊引擎,吃下那 4.5 tok/s
真相清楚了就好選。我把引擎換回舊的 ds4-186-524、跑 256K。取捨攤開來:
| Entrpi v0.3.0 | 舊 ds4-186-524(現在用) | |
|---|---|---|
| decode | ~20 tok/s | ~15.5 tok/s |
| prefill | 761 tok/s | 324 tok/s |
| 記憶體 headroom | ~2GiB | ~11GiB |
| 大 prompt | 211K 就 OOM | 68K 穩過、還剩 11GiB |
| 出事時 | OOM→systemd 兩分鐘重啟 | 不出事 |
實測換回去:decode 15.5(server log 15.57/15.51)、68K 的大 prefill 全程 available 穩在 11GiB、NRestarts=0。慢了那 4.5 tok/s,但再也不會跑一跑自己 OOM、卡你兩分鐘。
而且回頭想:我上次感覺「變慢」的體感,根本不是 decode 那 4.5 tok/s 的差,是那兩分鐘的 OOM 重啟。帳面上快、實際上被重啟吃掉——這種快,不要也罷。 user 裁「舊版 256K」,收工。
平心而論:這件事 Entrpi 真的沒藏,README 白紙黑字寫了那條 16K 的線;repo 也沒人回報這個 OOM。所以這不是維護者藏 bug,是我拿一顆為 16K 調的引擎去跑 20-30 倍的 context——我自己硬踩進去、README 早就寫清楚的 trade-off,不是等上游修就會好的東西。
Debug 區:給想自己查的人的碎念
前面看懂就夠用了。這段是留給想在自己機器上查同款問題的人——三個查的時候會卡住的地方。
① 查 runtime 別信報告,而且 log 可能根本不見。 現在跑的 server 的 stdout 被導到一個已經死掉的 tmux socket(/proc/<pid>/fd/1 -> socket:[...])——live log 看不到。我只能靠三樣東西拼出「現在跑的到底是哪顆引擎、卡在哪」:systemctl --user 的 journal、server 每筆 chat 自報的 timings(decode_tok_s/ttft_ms),還有 /proc/<pid>/exe 認 binary + cmdline 的 flag 指紋。中間我一度把 Entrpi 的 ds4-server 誤當成舊 binary——兩顆執行檔同名,是靠 --warm-weights/--power/--kv-disk-dir 這些 flag 指紋才分得出來。
② systemd unit 是 --user scope,不是 system。 我一開始 systemctl status ds4entrpi-8000 查,回「could not be found」,差點判定「這服務沒人看守、死了不會回來」。其實它是掛在 user scope(systemctl --user),active、enabled、Restart=on-failure 都好好的。查錯 scope 害我繞一圈。
③ warm-weights 做了什麼——為什麼我把它當實測 A/B、不當源碼證明。 這個 flag 對應 model_warm_weights(ds4.c:1915),會 MADV_WILLNEED 再逐頁 touch,開機時把整份 80GiB 權重 mmap 全部灌成常駐。但 runtime 走 device artifacts、根本不需要這份 mmap(啟動 log 那句 expert weights served from device artifacts 就是),所以把它整份灌進實體記憶體等於白吃 80GiB。我把 flag 移掉、日常 31K prefill 就從崩到穩。但我把這當實測 A/B、不是逐行源碼證明——這個 flag 確實會在開機把 80GiB fault-in 進實體記憶體,但壓力下確切的回收行為很細(clean 的 file-backed 頁其實兩邊都可回收,而且 glibc 上 posix_madvise(POSIX_MADV_DONTNEED) 是 no-op),我信量到的結果、不硬掰一條漂亮的因果鏈。它也擋不住 211K 那次 OOM,因為那份 78.71GiB artifacts 一直都在。
(順帶一提,GB10 的統一記憶體讓 RSS 這個數字很不可信:退化的時候 process RSS 只剩 791MiB,權重其實在 unified memory 裡、不算進 RSS。要判記憶體壓力,看 free 的 available 跟 journal 的 batch-fit 行,別看 RSS。)
帶走的三個重點
速度是有代價的,先問它拿什麼換。 Entrpi 的 +4 tok/s 是拿記憶體 headroom 換的。在一台本來就快滿的機器上,這筆帳我升級的時候沒算到——它把權重存了第二份。下次看到「同硬體、更快的引擎」,先問一句:它多用了多少記憶體?而且「快」通常只在一個範圍內快——Entrpi 是照 ~16K context 抓的,我卻拿它跑 20-30 倍還一臉驚訝。靠一個加速方案之前,先看清楚它是針對多大的 context 調校的。
帳面數字不等於體感。 我以為「變慢」是 decode 慢了,量了半天才發現真正拖垮體感的是那兩分鐘的 OOM 重啟。穩定,對日常 agent 流量來說,價值大於帳面 tok/s。 一顆穩穩 15.5 的腦,比一顆偶爾崩兩分鐘的 20,好用。
查機器要查 runtime 實況,別信報告。 memory 說現在跑的是 Entrpi、log 導去死掉的 socket、unit 藏在 user scope、RSS 在 UMA 上不可信——每一個都差點把我帶歪。最後是 server 自報的 timings、/proc/<pid>/exe、cmdline flag 指紋這些當下量到的事實把真相拼出來的。
這篇是 #9〈把 ds4 引擎換成 Entrpi、白賺半代升級〉 的誠實續集——上一篇我慶祝升級,這篇它來討債,而帳是我自己沒算到權重被存了兩份。
同系列其他篇 — DeepSeek-V4-Flash on DGX Spark
- #9:把 ds4 引擎換成 Entrpi,白賺半代速度 — 這篇的前傳:同模型只換引擎、decode 14-16→20 的那次升級。
- #8:評估 DeepSeek-V4-Flash 的優化宣稱 — 怎麼控制變因做 A/B test,驗證「更快」這個說法。
常見問題
- Entrpi 引擎升級後為什麼會 OOM?
- Entrpi 為了把 decode 從 14-16 拉到約 20 tok/s,啟動時把權重 repack 成一份 78.71GiB 的 device artifacts 給 CUDA 用——但原始的 80.76GiB mmap 還在。等於權重在記憶體裡存了兩份。DGX Spark 只有約 119GiB 可用,兩份一疊就把整台塞到只剩 2GiB。日常短請求還撐得住,一個大 prompt(我實測 211K token)的 prefill 尖峰一來就爆掉,被 OOM killer 砍掉,systemd 再花約兩分鐘自動重啟。舊的 ds4-186-524 引擎不 repack、權重只有一份,同一台就留得住約 11GiB headroom,不會 OOM。
- --warm-weights 這個 flag 會害 OOM 嗎?
- 本身不是主因,但會加壓——它是我第一條真的線索。`--warm-weights` 對應 `model_warm_weights`(ds4.c:1915),會用 MADV_WILLNEED 加逐頁 touch,把整份 80GiB 權重 mmap 在開機時全部灌成常駐。但 runtime 其實走 device artifacts、不需要這份 mmap,把它整份 fault-in 進實體記憶體等於白吃 80GiB。移掉這個 flag,日常 31K prefill 就從崩到穩——這是**實測結果**,確切的分頁機制我不硬掰(clean 的 file-backed 頁其實兩邊都可回收,而且 glibc 上 posix_madvise(DONTNEED) 是 no-op)。它擋不住 211K 那次 OOM——因為那份 78.71GiB artifacts 一直都在,跟這個 flag 無關。
- 降 context 或改 KV 精度能解 OOM 嗎?
- 幾乎沒用。DeepSeek-V4-Flash 的 KV 本來就很小(256K 才約 5.9GiB),而且 Entrpi 預設已經是 FP8 主 KV + FP4 indexer 壓縮,沒有更省的檔位好調。真正吃掉 119GiB 的是那份 78.71GiB 的權重 artifacts,不是 KV。降 512K→256K 省的 KV 約 5.4GiB(512K≈11.3、256K≈5.9GiB)——但整台是被那 ~40GiB 的重複權重擠爆的,砍 KV 幾乎動不了大局,大 prefill 照樣 OOM。要留活路只能換回單份權重的引擎,或忍受偶爾 OOM 重啟。
接著讀
- 2026-07-21[DeepSeek-V4-Flash] 一句好奇,單台 DGX Spark 上白賺半代引擎升級——ds4 換 Entrpi
我只是問 Codex『這個 ds4 repo 有沒有針對單台 GX10 的強化』,結果一個下午,我那台在跑正職的 DGX Spark 就換了引擎(antirez/ds4 的 Blackwell fork,Entrpi)。同一顆 abliterated 模型、什麼都沒換,decode 從 14-16 提到 20、讀 prompt 快約一倍。沿路還實測收掉三個真問題:投機解碼在 abliterated 上為什麼反而更慢、disk-KV 是不是在省記憶體、managed KV 是不是 swap。
- 2026-07-09[本地 LLM] 我把 FlashMemory 重訓到自己的 Q2 build 上,它還是改善不了 V4-Flash 的 native lightning indexer
DeepSeek-V4-Flash 本來就有一套 native lightning indexer,追真實 attention 追到 93–96%。FlashMemory 會先把候選 chunk 篩小一圈,但直接拿來用跟亂猜沒兩樣;我重訓到自己的 Q2 build 上,也只爬到 89–92%。GB10 上 NO-GO。
- 2026-07-07[地端 LLM] 284B 塞得進 128GB、長 context 還跑得動:DeepSeek-V4-Flash 打的是 KV cache,不是參數量
DeepSeek-V4-Flash 是 284B 的 MoE,長 context 還能在 128GB 的 GB10 上跑得動,靠的不是權重小,是它天生就在打 KV cache:混合式 attention(SWA + CSA + HCA)把 KV 壓到 64K 只剩 871MiB;lightning indexer 挑的是壓過的 entry、一步只讀幾百列。這篇拆 ds4 的實作,順便講為什麼你沒辦法再靠量化 KV 省記憶體。
- 2026-07-05[本地 LLM] 養了一個月,我的 284B agent 悄悄不再快取——ds4 的 evict 風暴,跟每輪重付的 prefill
Part 2 誇的『快取命中就順』hot path,一個月重度 agent 用下來悄悄失效。ds4 的 disk-KV cache 在 GB10 上被兩件事餓死:預算填滿的 evict 風暴,加上 tool-call 那輪根本沒存 checkpoint。一段 14K token 的對話只認得 268 個字,剩下整段重 prefill。這篇拆 log 指紋、上游 PR #489、跟一張證明機制修好的 A/B 驗收。
不想錯過新文章?
訂閱我確保不漏接!
隨時一鍵退訂。