<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RTX 5090 32G 由 llama.cpp 換 SGLang 跑本地 Qwen3.8-27B：DeepSeek Harness 輸出效能由 ~44 到 ~65–85 tok/s 的實測紀錄]]></title><description><![CDATA[<p dir="auto"><em>單併發場景下的引擎決策紀錄——為什麼換、換後得到什麼、以及踩到 VRAM 底線的真實轉折</em></p>
<p dir="auto">我的使用情境很清楚：<strong>單併發</strong>。一個人、一張 RTX 5090 32G，跑本地 Qwen3.8-27B 做 DeepSeek Harness（DSH）與 Codex 這類 agent 任務。llama.cpp 在這目標上已經很快（DSH 約 44 tok/s），換到 SGLang 後同卡同模型單人 decode 升到 65–85 tok/s。本篇記錄這次換引擎的真實經過，包含三個轉折——架設路線從 GPT 編譯失敗轉到 Docker 映像檔、DSpark 推測解碼因 VRAM 不足關掉、顯示器切到 CPU 內顯救回 1.4GB VRAM。</p>
<p dir="auto"><strong>結論先說</strong>：如果你的場景是「單人、單併發、要單人速度越快越好 + 多模態 + 256K 長上下文」，SGLang 比 llama.cpp 更快（DSH 44→65–85 tok/s，快 48–93%），且原生支援多模態與 agent tool-call。SGLang 的多工能力是附帶的，對純單併發不是必要賣點。真正決定 5090 32G 上跑得好不好的，是 <strong>VRAM 餘裕够不够</strong>——最後兩節會詳細講。</p>
<h2>為什麼換</h2>
<p dir="auto"><strong>1. llama.cpp 端，單人速度已經夠快，但多模態與 agent 架構是短板。</strong> 你的 llama.cpp 設定 <code>-np 1</code>，DSH 場景單人 decode 約 44 tok/s，對單併發來說已不算慢。問題不在速度，而在架構：llama.cpp 的多模態要靠外部 <code>--mmproj</code> 投影器、agent tool-call 要自己解析。換 SGLang 主要為了<strong>原生多模態、更強的 tool-call parser、以及單人 decode 速度</strong>，不是為了多工。</p>
<p dir="auto"><strong>2. SGLang 單人 decode 直接快 48–93%。</strong> 同卡同模型，SGLang 在 DSH 場景實測約 65 tok/s、Codex 場景約 85 tok/s，對比 llama.cpp 的 44 tok/s。原因不是多工，而是 NVFP4 權重更輕（省 ~7GB 全數餵給 KV cache 與狀態池）+ FlashInfer 後端 + RadixAttention 把同一次對話重複的 system prompt 不重算。對「單人跑 agent 長任務」這類場景，前綴重用是實打實的省時，跟多工無關。</p>
<p dir="auto"><strong>3. VRAM 帳本是單併發能不能撐住 237K 的關鍵。</strong> llama.cpp 端 Q5_K_XL 約 22–24GB，SGLang 端 NVFP4 約 16.5GB。省下的 7GB 全數餵 KV 與 Mamba 狀態。但 NVFP4 + 237K + Mamba 狀態池把 32GB 逼到邊——<strong>最後剩餘 VRAM 只剩 0.4GB</strong>。這正是後兩節的主角。</p>
<h2>換後得到什麼</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>面向</th>
<th>llama.cpp（Q5_K_XL）</th>
<th>SGLang（NVFP4）</th>
<th>差異本質</th>
</tr>
</thead>
<tbody>
<tr>
<td>模型量化</td>
<td>Q5_K_XL GGUF（~22–24GB）</td>
<td>NVFP4 W4A4（~16.5GB）</td>
<td>SGLang 省 ~7GB，全給 KV/狀態</td>
</tr>
<tr>
<td>上下文</td>
<td>256K（262,144）</td>
<td>237K（237,568）</td>
<td>接近，SGLang 略短但同級</td>
</tr>
<tr>
<td>KV cache 精度</td>
<td>Q8_0</td>
<td>FP8 E4M3</td>
<td>同級，FP8 為 Blackwell 原生</td>
</tr>
<tr>
<td>單人 decode（DSH）</td>
<td>~44 tok/s（實測）</td>
<td>~65 tok/s（DSH）/ ~85 tok/s（Codex）</td>
<td>SGLang 快 48–93%</td>
</tr>
<tr>
<td>前綴重用</td>
<td>無 Radix 快取</td>
<td>RadixAttention，同 system prompt 不重算</td>
<td>單人 agent 長任務也受惠</td>
</tr>
<tr>
<td>多模態</td>
<td>外部 <code>--mmproj</code> 投影器</td>
<td>原生支援，內建 pipeline</td>
<td>SGLang 較乾淨</td>
</tr>
<tr>
<td>冷啟動</td>
<td>快（GGUF mmap）</td>
<td>數十秒（NVFP4 + JIT）</td>
<td>llama.cpp 占優</td>
</tr>
<tr>
<td>多工併發</td>
<td>FIFO，<code>-np 1</code></td>
<td>continuous batching</td>
<td>單併發場景用不到</td>
</tr>
</tbody>
</table>
<blockquote>
<p dir="auto"><strong>重點澄清：多工不是這次換的原因。</strong> SGLang 的 continuous batching 能跑 4 併發 425 tok/s，在團隊共用場景很有價值——但<strong>我的場景是單併發</strong>。換 SGLang 贏在<strong>單人速度（44→65–85）</strong>、原生多模態、agent tool-call parser、前綴重用，這些跟多工無關。</p>
</blockquote>
<h2>架設路線的轉折：GPT 編譯失敗到 Docker 映像檔成功</h2>
<p dir="auto">在講 VRAM 之前，先講這段路——<strong>SGLang 本身很好架，但「怎麼架」差很多</strong>。早期我請 GPT 幫我建置 SGLang，它走的是<strong>模型編譯路線</strong>：從原始碼 build SGLang kernel、編譯 CUDA 扩展、對齊 Blackwell sm_120 的算子、處理 JIT 快取與版本對齊。這條路線<strong>極其複雜</strong>，中間踩了許多 Bug——編譯器版本對不上、CUDA 13 與 kernel 的 sm_120 不支援、JIT 快取缺失導致反覆重編、跑起來 CUDA graph 異常——<strong>最終都架設失敗</strong>。卡了相當久都沒有跑起來。</p>
<p dir="auto">後來在 <a href="https://www.youtube.com/channel/UC1BaDHNaNEvEGSxwK5wdgqw" rel="nofollow ugc">抡锤者 YouTube 頻道</a> 看到版主提到用 <strong>Docker + 官方映像檔</strong>的方式架設 SGLang——把整份 kernel 與 CUDA 工具鏈都打包進映像檔，只掛模型目錄與設定啟動參數，不用自己 build 任何東西。這個做法<strong>大幅簡化</strong>架設流程：拉映像檔 → 掛載模型 → 一條 <code>python3 -m sglang.launch_server</code> 起來。照著做，才真正把 SGLang 架起來。之後才能往下調 NVFP4、DSpark、VRAM 這些事。</p>
<blockquote>
<p dir="auto"><strong>給同樣想架 SGLang 的人</strong>：5090 / Blackwell sm_120 上，<strong>用官方 Docker 映像檔</strong>比從原始碼編譯省事太多。原始碼路線要處理 CUDA 版本對齊、kernel JIT 快取、sm_120 算子支援，Bug 密度高、時間成本高。官方映像檔把這些都封裝好了，你只負責掛模型與啟動參數。抡锤者頻道那篇是關鍵轉折，省了幾周的踩坑時間。</p>
</blockquote>
<h2>兩個 VRAM 踩底線的轉折</h2>
<p dir="auto">架起來之後，才開始面對 VRAM 這件事。這是 32GB 顯卡跑 27B 稠密模型最現實的限制。</p>
<h3>轉折一：DSpark 推測解碼因 VRAM 不足關掉</h3>
<p dir="auto">SGLang 支援 DSpark（block-diffusion 推測解碼，同 DFlash2 路線），開了能把單人 decode 再推 ~10–15%。我原本要開，但載入 draft 模型後<strong>剩餘 VRAM 直接逼近 0，<code>max_total_num_tokens</code> 被擠到只剩約 94K</strong>——原本 237K 的上下文池被 DSpark 的 draft 模型與額外狀態池吃掉一大半，對單人跑 agent 長任務來說可用上下文砍到不足一半，代價太大了。最終於是把 DSpark 關掉，<strong>速度由開 DSpark 的 ~95 tok/s 掉到 ~85 tok/s，每秒差 10 tok/s、約 11%</strong>。差異不大，但換來的是 <strong><code>max_total_num_tokens</code> 恢復到 317,282（約 310K）、KV 池與 Mamba 狀態池全數還給模型、不再 OOM</strong>。關掉 DSpark 不只省 VRAM，等於白捡 170K+ 的可用上下文——這在單併發、要 256K 長任務的場景比 10 tok/s 的速度差重要得多。</p>
<blockquote>
<p dir="auto"><strong>實測數字</strong>：開 DSpark 時 <code>max_total_num_tokens</code> 只剩 ~94K、~95 tok/s；關掉 DSpark 後 <code>max_total_num_tokens=317,282</code>（約 310K）、~85 tok/s。速度差 10 tok/s / 約 11%，但可用上下文由 94K 暴增到 317K——對單併發 256K 長任務，關 DSpark 是正確取捨。</p>
</blockquote>
<h3>轉折二：顯示器切到 CPU 內顯，救回 1.4GB VRAM</h3>
<p dir="auto">關掉 DSpark 後，剩餘 VRAM 仍只有 0.4GB，SGLang 長時間跑 agent 任務偶爾還是會被 OOM 打斷。關鍵洞察是：<strong>電腦的顯示輸出佔用 NVIDIA 顯示卡的 VRAM</strong>——桌面 compositor、多解析度 monitor、desktop window manager 全數吃 5090 的 VRAM。我把顯示輸出從 RTX 5090 切到 i9-14900K 內建的 Intel UHD 770，<strong>剩餘 VRAM 從 0.4GB 一路漲到 1.8GB，多了 1.4GB 餘裕</strong>。這 1.4GB 讓 SGLang 的 KV 池與 Mamba 狀態池都穩住，長時間跑 DSH 不再被 OOM。這是 5090 32G 跑 27B 時最划算的「免費」VRAM——不用降量化、不用縮上下文，只要把顯示器從 NVIDIA 切到 CPU 內顯。</p>
<blockquote>
<p dir="auto"><strong>這招值得抄</strong>：RTX 5090 32G 跑 27B 稠密模型時，把顯示輸出切到 CPU 內顯（UHD 770 或同級），可把被桌面 compositor 吃掉的 VRAM 還給模型池。實測剩餘 VRAM 從 0.4GB 升到 1.8GB，對長時間 agent 任務穩定度幫助很大。</p>
</blockquote>
<h2>兩點誠實標註</h2>
<p dir="auto"><strong>1. 量化不同，不是 apples-to-apples。</strong> Q5_K_XL vs NVFP4 意味著「同卡同上下文」比較時，SGLang 端是 4-bit 權重。要嚴格比同量化，需另跑 SGLang GPTQ/AWQ 5-bit——但 NVFP4 是 Blackwell 上 27B 進 32GB 的標準答案。文中所有 SGLang 數字都基於 NVFP4 checkpoint（RadixArk，Apache 2.0）。</p>
<p dir="auto"><strong>2. 85 tok/s 是「關 DSpark、切內顯後」的穩定值，且可用上下文最大。</strong> 開 DSpark 時 ~95 tok/s，但 <code>max_total_num_tokens</code> 被擠到 ~94K；關掉後 <code>max_total_num_tokens=317,282</code>（約 310K）、~85 tok/s。對單併發 256K 長任務，關 DSpark 換回 317K 可用上下文是正確取捨，速度只少 11%。</p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/b18654ac-93ea-4c1a-a178-4ff99123fae9.png" alt="螢幕擷取畫面 2026-08-31 033604.png" class=" img-fluid img-markdown" /></p>
<h2>成本</h2>
<ul>
<li><strong>硬件</strong>：0（同卡，顯示器切到已有 CPU 內顯，不需新硬體）</li>
<li><strong>模型權重</strong>：NVFP4 checkpoint 免費（RadixArk，Apache 2.0）</li>
<li><strong>架設</strong>：從 GPT 編譯路線（失敗）轉到 Docker 映像檔（成功），省了幾周踩坑時間；SGLang 官方映像檔免費</li>
<li><strong>VRAM 取捨</strong>：為求穩定與可用上下文關掉 DSpark（~95→~85 tok/s，<code>max_total_num_tokens</code> 由 ~94K 恢復到 317,282），再切內顯救回 1.4GB VRAM（剩餘 0.4→1.8GB）</li>
<li><strong>機會成本</strong>：換掉的是 llama.cpp「單人最易調、冷啟動最快」的優勢；SGLang 旗標多、Mamba GDN 混合架構在 5090 上有已知的 prefill CUDA graph hang（需 <code>--disable-prefill-cuda-graph</code> 或新版本已修），要跟著版本走</li>
</ul>
<h2>你的兩組實際執行參數</h2>
<h3>llama.cpp（Q5_K_XL + Q8_0 KV + 256K）</h3>
<pre><code>llama-server.exe ^
  -m "D:\AI\llama.cpp\models\Qwen3.8-27B\Qwen3.8-27B-UD-Q5_K_XL.gguf" ^
  --mmproj "D:\AI\llama.cpp\models\Qwen3.8-27B\mmproj-F16.gguf" ^
  --alias qwen38-27b-q5 ^
  --host 0.0.0.0 --port 8080 ^
  -ctk q8_0 -ctv q8_0 ^
  -c 262144 ^
  -ngl 99 ^
  -b 4096 -ub 256 ^
  -np 1 ^
  -t 32
</code></pre>
<p dir="auto"><strong>參數說明</strong>：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>旗標</th>
<th>值</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-m</code></td>
<td>Q5_K_XL GGUF</td>
<td>Unsloth 動態量化 Q5_K_XL 版，約 22–24GB，5090 32G 上最佳品質/體積平衡</td>
</tr>
<tr>
<td><code>--mmproj</code></td>
<td>mmproj-F16.gguf</td>
<td>多模態視覺投影器（F16），啟用圖片輸入（發票、產品圖、文件 OCR）</td>
</tr>
<tr>
<td><code>-ctk</code> / <code>-ctv</code></td>
<td>q8_0</td>
<td>K/V cache 都用 Q8_0。Q8 比 Q4 在 agent 多輪任務中智能明顯更好（已實測）</td>
</tr>
<tr>
<td><code>-c 262144</code></td>
<td>256K</td>
<td>原生 256K，Q8_0 KV 約 16–18GB，加 22–24GB 權重，32GB 剛好容納</td>
</tr>
<tr>
<td><code>-ngl 99</code></td>
<td>99 層上 GPU</td>
<td>全層離到 GPU，CPU fallback 0</td>
</tr>
<tr>
<td><code>-b 4096 -ub 256</code></td>
<td>batch 4096 / ubatch 256</td>
<td>prompt 處理 batch 4096、細分 256，平衡長 prefill 速度與記憶體尖峰</td>
</tr>
<tr>
<td><code>-np 1</code></td>
<td>1 parallel slot</td>
<td>單工排程，對單併發場景正確</td>
</tr>
<tr>
<td><code>-t 32</code></td>
<td>32 執行緒</td>
<td>CPU 側前處理/排程執行緒數</td>
</tr>
</tbody>
</table>
<h3>SGLang（NVFP4 + FP8 KV + 237K + FlashInfer，DSpark 關、顯示切內顯）</h3>
<pre><code>python3 -m sglang.launch_server \
  --model-path /model \
  --served-model-name qwen38-27b-nvfp4 \
  --host 0.0.0.0 --port 30000 \
  --trust-remote-code \
  --context-length 237568 \
  --mem-fraction-static 0.92 \
  --kv-cache-dtype fp8_e4m3 \
  --attention-backend flashinfer \
  --mm-feature-transport cpu \
  --max-running-requests 1 \
  --max-mamba-cache-size 5 \
  --mamba-radix-cache-strategy extra_buffer_lazy \
  --enable-cache-report \
  --reasoning-parser qwen3 \
  --tool-call-parser qwen3_coder
</code></pre>
<p dir="auto"><strong>參數說明</strong>（單併發導向）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>旗標</th>
<th>值</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--model-path</code></td>
<td>/model</td>
<td>容器掛載的 NVFP4 checkpoint（RadixArk，W4A4 約 16.5GB）</td>
</tr>
<tr>
<td><code>--context-length</code></td>
<td>237568</td>
<td>237K，NVFP4 省下 VRAM 全數餵 KV 後的可用上限</td>
</tr>
<tr>
<td><code>--mem-fraction-static</code></td>
<td>0.92</td>
<td>92% 顯存劃給 KV/狀態池（關 DSpark 後 0.92 夠用）</td>
</tr>
<tr>
<td><code>--kv-cache-dtype</code></td>
<td>fp8_e4m3</td>
<td>FP8 E4M3 KV，Blackwell 原生，與 llama.cpp Q8_0 同級</td>
</tr>
<tr>
<td><code>--attention-backend</code></td>
<td>flashinfer</td>
<td>FlashInfer 後端，5090 上單人 decode 速度最佳選項之一</td>
</tr>
<tr>
<td><code>--mm-feature-transport</code></td>
<td>cpu</td>
<td>多模態特徵走 CPU，省 GPU 記憶體</td>
</tr>
<tr>
<td><code>--max-running-requests</code></td>
<td>1</td>
<td><strong>單併發</strong>——對照 llama.cpp 的 <code>-np 1</code>，符合實際使用情境</td>
</tr>
<tr>
<td><code>--max-mamba-cache-size</code></td>
<td>5</td>
<td>Qwen3.8 hybrid GDN/Mamba 架構，state slot 上限 5</td>
</tr>
<tr>
<td><code>--mamba-radix-cache-strategy</code></td>
<td>extra_buffer_lazy</td>
<td>Mamba 狀態 lazy 策略，每請求狀態成本 5→4 slot，同精度省 VRAM</td>
</tr>
<tr>
<td><code>--enable-cache-report</code></td>
<td>—</td>
<td>回報 RadixAttention 前綴命中，驗證 prefix reuse 效益</td>
</tr>
<tr>
<td><code>--reasoning-parser</code> / <code>--tool-call-parser</code></td>
<td>qwen3 / qwen3_coder</td>
<td>解讀 Qwen3 思維鏈與 tool-call 結構化輸出</td>
</tr>
</tbody>
</table>
<h3>若要重開 DSpark（需 VRAM 夠、且接受 94K 上下文上限）</h3>
<pre><code>  --speculative-algorithm DFLASH \
  --speculative-draft-model-path incoai/Qwen3.8-27B-DFlash2 \
  --speculative-num-draft-tokens 8 \
  --mem-fraction-static 0.91 \
  --chunked-prefill-size 1024
</code></pre>
<p dir="auto">加上這四行可把 ~85 tok/s 推回 ~95 tok/s。但<strong>代價是 <code>max_total_num_tokens</code> 由 317,282 縮回 ~94K</strong>——32G 上 27B + DSpark draft 模型會擠爆 KV 池，可用上下文砍到 94K，對 256K 長任務不夠。若要在你的配置重開 DSpark，<strong>先切內顯把那 1.4GB 救回來、且接受 94K 上下文上限</strong>，再試；否則單併發長任務場景關著比較划算。</p>
<h2>決策速查（單併發導向）</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>你的場景</th>
<th>用誰</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td>單人、單併發、快冷啟動、最簡單調參</td>
<td>llama.cpp</td>
<td>GGUF mmap 秒開、調參最少、Q5_K_XL 品質最熟</td>
</tr>
<tr>
<td>單人、單併發、要單人 decode 更快</td>
<td>SGLang</td>
<td>DSH 實測 65–85 vs llama.cpp 44，快 48–93%</td>
</tr>
<tr>
<td>單人 + 多模態（發票/產品圖 OCR）</td>
<td>SGLang</td>
<td>原生多模態 pipeline，不用外部 mmproj</td>
</tr>
<tr>
<td>單人 + agent tool-call 長任務</td>
<td>SGLang</td>
<td>內建 <code>--tool-call-parser qwen3_coder</code> + RadixAttention 前綴重用</td>
</tr>
<tr>
<td>要重開 DSpark 衝 95 tok/s</td>
<td>先切內顯</td>
<td>先把 VRAM 餘裕從 0.4 救到 1.8，再開 DSpark 才撐得住；且要接受 max_total_num_tokens 縮回 94K</td>
</tr>
<tr>
<td>想架 SGLang 但不會編譯</td>
<td>Docker 映像檔</td>
<td>比 GPT 原始碼編譯省事，省幾周踩坑時間</td>
</tr>
</tbody>
</table>
<hr />
<p dir="auto"><em>數據來源：llama.cpp 端為用戶 RTX 5090 實測（DeepSeek Harness 場景 Q5_K_XL + Q8_0 KV + 256K，約 44 tok/s）。SGLang 端為用戶 RTX 5090 實測（NVFP4 checkpoint，DSH 約 65 tok/s、Codex 約 85 tok/s；開 DSpark 時 ~95 tok/s 但 max_total_num_tokens 僅 ~94K，關掉後 ~85 tok/s、max_total_num_tokens=317,282；顯示器切到 i9-14900K 內建 Intel UHD 770 後剩餘 VRAM 由 0.4GB 升至 1.8GB；架設路線由 GPT 原始碼編譯失敗轉為 Docker 官方映像檔成功，來源：抡锤者 YouTube 頻道）。所有 SGLang 數字基於 NVFP4 W4A4 checkpoint（RadixArk，Apache 2.0），與 llama.cpp 端 Q5_K_XL 量化不同，比較時請注意此差異。DSpark 為 z-lab / <a href="http://inco.ai" rel="nofollow ugc">inco.ai</a> 提供的 lossless block-diffusion 推測解碼，greedy 輸出與目標模型完全相同。</em></p>
]]></description><link>https://lcz.me/topic/1425</link><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 22:53:44 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/1425.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 30 Aug 2026 19:55:51 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to RTX 5090 32G 由 llama.cpp 換 SGLang 跑本地 Qwen3.8-27B：DeepSeek Harness 輸出效能由 ~44 到 ~65–85 tok/s 的實測紀錄 on Sun, 30 Aug 2026 22:05:49 GMT]]></title><description><![CDATA[<p dir="auto">总结的很好。这不是吐字速度的差异，吐字速度没那么重要，重要的是缓存效率大大提升，每次只计算需要算增量部分。</p>
]]></description><link>https://lcz.me/post/15019</link><guid isPermaLink="true">https://lcz.me/post/15019</guid><dc:creator><![CDATA[terry]]></dc:creator><pubDate>Sun, 30 Aug 2026 22:05:49 GMT</pubDate></item></channel></rss>