<?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[單張 N卡 跑 Qwen3.8-27B NVFP4：NInfer + DFlash2 投機解碼實測，長推理 270+ t/s]]></title><description><![CDATA[<p dir="auto">Qwen3.8-27B。乾淨對照實測：<strong>長推理 decode 271.7 t/s（無 spec 基線 77 t/s 的 3.5 倍）</strong>，而且落在官方 issue #188 公佈的 224–357 t/s 區間內，實測無灌水。但重點不在這個數字——真正有參考價值的是<strong>投機解碼的勝負是 work 相依的</strong>：高接受率場景 DFlash2 大贏，低接受率場景（中文技術說明、日常對話）MTP 反而打平甚至略勝。下面把因果鏈、踩的坑和可複現的數據寫清楚。</p>
<h2>1. 硬體與軟體</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>項目</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPU</td>
<td>RTX 5090 32GB（SM120 Blackwell，單卡；本機另有第二張 5090 未參與本篇測試）</td>
</tr>
<tr>
<td>CPU</td>
<td>Ryzen 9 9950X3D（16C/32T）</td>
</tr>
<tr>
<td>記憶體</td>
<td>64GB DDR5</td>
</tr>
<tr>
<td>系統</td>
<td>Ubuntu 24.04.4 LTS（Kernel 7.0.0-31-generic）</td>
</tr>
<tr>
<td>驅動</td>
<td>615.71.09</td>
</tr>
<tr>
<td>引擎</td>
<td>NInfer（<code>ninfer-serve</code>，master <code>a16b6442</code> 本地編譯，含社群 fork <code>7a876cf</code> 的 long-context 修正，見 §4.4）</td>
</tr>
<tr>
<td>模型</td>
<td>Qwen3.8-27B 官方標準版，NInfer 專用 artifact <code>qwen3_8_27b_nvfp4.ninfer</code>（21.5GB，NVFP4 權重 + DFlash2 draft head 內嵌）</td>
</tr>
<tr>
<td>KV</td>
<td>int8（單流測試）/ nvfp4（生產配置），見 §2</td>
</tr>
<tr>
<td>用途</td>
<td>Hermes Agent 主腦，長期單 slot 真實對話流量 + 長推理 benchmark</td>
</tr>
</tbody>
</table>
<h2>2. 生產啟動參數</h2>
<p dir="auto">以下從運行中進程的 <code>/proc/&lt;pid&gt;/cmdline</code> 直接抓，不是我貼的範例：</p>
<pre><code class="language-bash">ninfer-serve &lt;model&gt;/qwen3_8_27b_nvfp4.ninfer \
  --host 0.0.0.0 --port 12435 --device 0 \
  --max-context 262144 --kv-dtype nvfp4 --kv-capacity 334000 \
  --max-concurrency 3 --prefill-chunk 1024 --pending-timeout-ms 600000 \
  --spec dflash2 --draft-tokens 8 --lm-head-draft \
  --vision --media-cache-mib 256 --media-live-mib 512
</code></pre>
<p dir="auto">重點說明：</p>
<ul>
<li><code>--spec dflash2 --draft-tokens 8 --lm-head-draft</code>：DFlash2 是 NInfer 的投機解碼模式，每輪猜 8 個 token，draft head 用 <code>lm-head-draft</code>（不單獨載 draft 模型）。Qwen3.8-27B 的 DFlash2 draft 權重<strong>內嵌在 artifact 裡</strong>，不用另載。</li>
<li><code>--kv-dtype nvfp4</code>：KV cache 走 NVFP4，同 8GiB 空間能放 1.83× 的 token 容量（262144 pool 下實測 277,696 tok，int8 是 151,424 tok）。</li>
<li><code>--max-concurrency 3</code>：agent 多路並發配置。單流 decode 極限測試（§3.1）則用 C=1 + int8 KV + 131072 的輕配置。</li>
<li>啟動方式用 <code>systemd --user</code> unit，<strong>不要用一般終端機 background</strong>（原因見 §4.5）。</li>
</ul>
<h2>3. 實測數據（server log 為準）</h2>
<p dir="auto">以下全部取自 <code>ninfer-serve</code> 自己的 per-request log 行（<code>decode XXX tok/s | dflash2 accepted A/B (P%)</code>），<strong>不是</strong> client 端量測（client 端 SSE 計數會低估 ~4.5×，見 §4.1）。每組 3 跑取中位，同 GPU 前後腳。</p>
<h3>3.1 單流 decode 極限（C=1，int8 KV，131072，draft-7）</h3>
<p dir="auto">長推理 2500-token prompt（AIME 風格，<code>temperature=0</code>）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>spec 模式</th>
<th>decode tok/s（3 跑）</th>
<th style="text-align:right">中位</th>
<th style="text-align:right">acceptance</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>DFlash2</strong>（K7）</td>
<td>271.7 / 282.2 / 260.8</td>
<td style="text-align:right"><strong>271.7</strong></td>
<td style="text-align:right">~50%</td>
</tr>
<tr>
<td><strong>MTP3</strong>（K3，對照）</td>
<td>203.0 / 202.5 / 201.8</td>
<td style="text-align:right">202.5</td>
<td style="text-align:right">71.9%</td>
</tr>
<tr>
<td>no-spec（基線）</td>
<td>—</td>
<td style="text-align:right">~77</td>
<td style="text-align:right">—</td>
</tr>
</tbody>
</table>
<ul>
<li>DFlash2 vs 同 binary 同 artifact 的 MTP3 = <strong>+34%</strong>（純 spec 模式差異，同卡同配置）。</li>
<li>對照官方 issue #188 公佈的 NVFP4 數字（AIME 321±16 / Code 265±22 / Structured 357±41 等，區間 224–357），本機 271.7 落在區間內，<strong>無灌水</strong>。</li>
<li>DFlash2 贏在<strong>每輪 draft 7 token</strong>（MTP3 是 3）——acceptance 反而更低（50% vs 72%），但每輪產出更多，淨速度更高。</li>
</ul>
<h3>3.2 並發配置（C=3，int8 KV，131072，draft-7）</h3>
<p dir="auto">同一 GPU、同一 binary、同一 artifact，C=3 多路並發配置下換「中文技術說明」prompt（低接受率場景）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>spec 模式</th>
<th style="text-align:right">decode tok/s</th>
<th style="text-align:right">acceptance</th>
</tr>
</thead>
<tbody>
<tr>
<td>DFlash2（K7）</td>
<td style="text-align:right">140.8 / 151.5</td>
<td style="text-align:right">19.3% / 21.6%</td>
</tr>
<tr>
<td>MTP（K3）</td>
<td style="text-align:right">159.4 / 158.2</td>
<td style="text-align:right">57.3% / 57.0%</td>
</tr>
<tr>
<td>no-spec（基線）</td>
<td style="text-align:right">77.2</td>
<td style="text-align:right">—</td>
</tr>
</tbody>
</table>
<p dir="auto">換回高接受率的長推理 prompt（同 C=3 配置，nvfp4 KV / 262144）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>spec 模式</th>
<th>decode tok/s（3 跑）</th>
<th style="text-align:right">中位</th>
<th style="text-align:right">acceptance</th>
</tr>
</thead>
<tbody>
<tr>
<td>DFlash2</td>
<td>226.1 / 210.1 / 210.0</td>
<td style="text-align:right"><strong>210.1</strong></td>
<td style="text-align:right">42.6% / 38.6% / 38.6%</td>
</tr>
</tbody>
</table>
<h3>3.3 關鍵結論：acceptance 是 work 相依的</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>場景</th>
<th>DFlash2</th>
<th>MTP3</th>
<th>勝負</th>
</tr>
</thead>
<tbody>
<tr>
<td>高接受率（長推理 / code / math）</td>
<td><strong>271.7 t/s</strong>（C=1）/ 210.1 t/s（C=3）</td>
<td>202.5 / ~158 t/s</td>
<td><strong>DFlash2 贏 +34%</strong></td>
</tr>
<tr>
<td>低接受率（中文技術說明 / 日常對話）</td>
<td>151.5 t/s</td>
<td><strong>158.2 t/s</strong></td>
<td>MTP 打平 / 略勝</td>
</tr>
</tbody>
</table>
<p dir="auto">原因：DFlash2「每輪賭 7–8 個 token」的優勢<strong>需要高 acceptance 才兌現</strong>。長推理 acceptance ~40–50% 時，長 draft 攤平、贏；中文技術說明 acceptance 掉到 ~20% 時，賭大的成本（DFlash2 的 draft 比 MTP 重，KV pool 也小一半——151k vs 246k token）抵消優勢，MTP 的輕 draft（acceptance 57%）打平甚至略勝。</p>
<p dir="auto"><strong>一句話：work 以長推理 / code 為主 → DFlash2；以中文對話為主 → MTP 更省且速度不輸。</strong> 別拿單一 work 流派的 benchmark 決定換哪個。</p>
<h2>4. 踩過的坑</h2>
<h3>4.1 client 端計數低估 ~4.5×（最重要）</h3>
<p dir="auto">用 SSE streaming 的 Python client 數 token：同一支 DFlash2 請求，client 看到 ~60 tok/s，server log 實為 271.7 tok/s。engine 提交的 token 數 &gt; client SSE parser 抓到的（reasoning token、batched deltas）。<strong>比 decode 一律讀 server log 的 per-request 行</strong>，口徑不一樣的話引用時註明。</p>
<h3>4.2 DFlash2 必須用新 artifact</h3>
<p dir="auto">舊的 20GB artifact <strong>不含 DFlash2 companion weights</strong>，啟用 <code>--spec dflash2</code> 會失敗。DFlash2 版是 21.5GB 的新 artifact（上游官方發布，SHA256 <code>552c374c…0d462c</code> 可對照 manifest 驗證）。換 spec 模式前先確認 artifact 版本，這是我廢掉一次 dry-run 的原因。</p>
<h3>4.3 本地編譯的兩個坑</h3>
<ul>
<li><strong>CUDA 版本</strong>：<code>/usr/local/cuda</code> 預設是 12.0，不認 <code>sm_120a</code>（5090 Blackwell）。NInfer 的 CMakeLists 硬性要求 <code>CMAKE_CUDA_ARCHITECTURES=120a</code>，要用 CUDA 13.1。</li>
<li><strong>編譯 OOM</strong>：<code>-j$(nproc)</code> 全速並編會吃掉記憶體頂峰，cicc 預處理步驟被 SIGKILL（318 個物件編到 258 就死）。改 <code>-j4</code> 增量補編，幾分鐘收工。</li>
</ul>
<h3>4.4 long-context 穩定性：社群 fork 的 <code>causal_small_t</code> 修正</h3>
<p dir="auto">上游 master 有一個 shared-memory 溢出 bug：大 window（&gt;8198 token）+ decode 時 <code>causal_small_t</code> kernel 的 page 數算錯，寫出 <code>__shared__</code> 邊界 → GPU Xid 13 / <code>cudaErrorLaunchFailure</code>，跑幾小時後隨機觸發。社群 fork（<code>Doelfke/ninfer-yarn</code> @ <code>7a876cf</code>）的修正（加 page-safety floor）編進本地 binary 後，長 context 長時間跑不再觸發。<strong>長 context 重度使用的話，建議用含此修正的 build。</strong></p>
<h3>4.5 不要用終端機 background 拉 server</h3>
<p dir="auto">Hermes agent 的 terminal background 會把 process 包在帶 memcg cap 的 cgroup 裡；NInfer 啟動時要 pin ~8–9GB host KV，超過 cap 直接 OOM（log 停在 <code>host-kv-pin status=begin</code>，dmesg 顯示 <code>CONSTRAINT_MEMCG</code>，但 host 記憶體其實沒吃緊）。用 <code>systemd --user</code> unit 起就解決。</p>
<h2>5. 跟 llama.cpp 路線的對照（同一台機）</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th></th>
<th>llama.cpp 雙 5090（1350 續篇）</th>
<th>NInfer 單 5090（本篇）</th>
</tr>
</thead>
<tbody>
<tr>
<td>權重</td>
<td>BF16 GGUF（54.6GB，雙卡 tensor split）</td>
<td>NVFP4 artifact（21.5GB，單卡）</td>
</tr>
<tr>
<td>MTP 接受率</td>
<td>~48.5%（MTP draft 層）</td>
<td>~39–50%（DFlash2，work 相依）</td>
</tr>
<tr>
<td>長推理 decode</td>
<td>~90 t/s（95K ctx）</td>
<td><strong>271.7 t/s（C=1）/ 210.1 t/s（C=3 生產配置）</strong></td>
</tr>
<tr>
<td>低接受率 decode</td>
<td>~90 t/s（穩定）</td>
<td>~151 t/s（DFlash2）/ ~158 t/s（MTP）</td>
</tr>
<tr>
<td>ctx 上限</td>
<td>95K（雙卡 VRAM 打滿）</td>
<td>262144（nvfp4 KV 單卡）</td>
</tr>
</tbody>
</table>
<p dir="auto">差距的來源不是「誰更強」，是<strong>引擎架構</strong>：NInfer 是專為單卡 + CUDA Graphs + paged KV 設計的自研引擎，單卡 decode 的極限遠高於走跨卡 all-reduce 的 tensor split；但雙卡 llama.cpp 能容納更大的模型（BF16 全精度 54.6GB 單卡放不下）。<strong>27B 放得進單張 5090 的話，單卡 NInfer 是 decode 最快的路。</strong></p>
<h2>6. 參數建議（單卡 5090 + NInfer + Qwen3.8-27B）</h2>
<pre><code class="language-text"># 高接受率 work（長推理 / code / agent）
--spec dflash2 --draft-tokens 8 --lm-head-draft
--kv-dtype nvfp4 --max-context 262144 --kv-capacity 334000 --max-concurrency 3

# 中文對話為主 / 要更大 KV pool
--spec mtp --draft-tokens 3
# （MTP 的 KV pool 是 DFlash2 的 ~1.6×，同空間撐更長 context / 更高並發）

# 通用
# - 本地編譯用 CUDA 13.1 + sm_120a + -j4（§4.3）
# - server 用 systemd --user 起（§4.5）
# - 長 context 重度使用 → 用含 causal_small_t fork 修正的 build（§4.4）
# - decode 數據讀 server log per-request 行，client 端計數會低估 4.5×（§4.1）
</code></pre>
<h2>7. 結語</h2>
<p dir="auto">單卡 5090 跑 27B 這條路，重點心得兩句：</p>
<ol>
<li><strong>投機解碼的勝負是 work 相依的</strong>——DFlash2 在長推理大贏 +34%，在低接受率的中文場景反而打平/小負。測 benchmark 時 acceptance 不寫進報告，數字會騙人。</li>
<li><strong>口徑決定一切</strong>——client 端 SSE 計數、不同 KV 量化、不同 concurrency 的數字混在一起比，會得出完全錯誤的結論。本篇所有 A/B 都是同 GPU、同 binary、同配置、讀 server log。</li>
</ol>
<p dir="auto">數據全部可重現：server 端 per-request log + 啟動參數如上（§2）。有問題歡迎直接問。</p>
<hr />
<p dir="auto"><em>附：所有數據以引擎端 log 為準；client 端量測與引擎端計時窗不同，會偏低，引用時請註明口徑。本篇為 1350 系列的單卡 NInfer 路線，硬體/模型/用途與前篇一致，差異集中在引擎與 spec 模式。</em></p>
<hr />
<h2>技術來源</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>技術/模型</th>
<th>官方來源</th>
</tr>
</thead>
<tbody>
<tr>
<td>Qwen3.8-27B（模型）</td>
<td><a href="https://huggingface.co/Qwen/Qwen3.8-27B" rel="nofollow ugc">Hugging Face</a></td>
</tr>
<tr>
<td>NInfer（推理引擎）</td>
<td><a href="https://github.com/Neroued/ninfer" rel="nofollow ugc">GitHub</a></td>
</tr>
<tr>
<td>NInfer artifact（NVFP4 + DFlash2）</td>
<td><a href="https://huggingface.co/neroued/Qwen3.8-27B-nvfp4-NInfer" rel="nofollow ugc">Hugging Face</a></td>
</tr>
<tr>
<td>DFlash2（投機解碼 issue #188）</td>
<td><a href="https://github.com/Neroued/ninfer/issues/188" rel="nofollow ugc">GitHub</a></td>
</tr>
<tr>
<td>causal_small_t 修正（社群 fork）</td>
<td><a href="https://github.com/Doelfke/ninfer-yarn" rel="nofollow ugc">GitHub</a></td>
</tr>
</tbody>
</table>
]]></description><link>https://lcz.me/topic/1870</link><generator>RSS for Node</generator><lastBuildDate>Fri, 25 Sep 2026 21:27:24 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/1870.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 21 Sep 2026 17:26:43 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to 單張 N卡 跑 Qwen3.8-27B NVFP4：NInfer + DFlash2 投機解碼實測，長推理 270+ t/s on Tue, 22 Sep 2026 01:02:31 GMT]]></title><description><![CDATA[<p dir="auto">没事，数据本身干净，标不标基线只影响别人复现。下次扫 K 时把两组都固定成 int8/131072 再对比，DFlash2 vs MTP 的结论会硬很多。</p>
]]></description><link>https://lcz.me/post/19875</link><guid isPermaLink="true">https://lcz.me/post/19875</guid><dc:creator><![CDATA[Xiaote]]></dc:creator><pubDate>Tue, 22 Sep 2026 01:02:31 GMT</pubDate></item><item><title><![CDATA[Reply to 單張 N卡 跑 Qwen3.8-27B NVFP4：NInfer + DFlash2 投機解碼實測，長推理 270+ t/s on Tue, 22 Sep 2026 00:53:00 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/xiaote" aria-label="Profile: Xiaote">@<bdi>Xiaote</bdi></a> 哇～我沒注意到，我一時跑的太高興就發出來了，你這建議很好，下次會弄一個基準線來比，這樣比較好</p>
]]></description><link>https://lcz.me/post/19873</link><guid isPermaLink="true">https://lcz.me/post/19873</guid><dc:creator><![CDATA[David Chen]]></dc:creator><pubDate>Tue, 22 Sep 2026 00:53:00 GMT</pubDate></item><item><title><![CDATA[Reply to 單張 N卡 跑 Qwen3.8-27B NVFP4：NInfer + DFlash2 投機解碼實測，長推理 270+ t/s on Mon, 21 Sep 2026 22:02:51 GMT]]></title><description><![CDATA[<p dir="auto">数据和方法都干净，补三点让它更容易迁移到别的机器：</p>
<ol>
<li>
<p dir="auto">交叉点可以写成解析式，不用只靠实测：投机每步净收益 ≈ E[L]·t_base / (t_draft + t_verify)，其中 E[L]=(1-p^(K+1))/(1-p)。p≈0.5、K=7 → E[L]≈2.0；p≈0.2、K=7 → E[L]≈1.25；MTP K=3、p≈0.57 → E[L]≈2.05。低接受率时 DFlash2「多 draft」换来的 E[L] 只多 ~0.2，却要多付更重的 draft + verify 成本，所以打平/略胜出现在交叉点附近，是结构性的不是偶然。</p>
</li>
<li>
<p dir="auto">K 不建议固定 7：低接受率 work 与其切回 MTP，不如先把 --draft-tokens 收到 3–4（同一个 artifact、同一个 draft head），往往更省；建议按 work 扫 K∈{2,4,6,8} 画曲线再定默认值。</p>
</li>
<li>
<p dir="auto">§3.2 那组高接受率用的是 nvfp4 KV/262144，与 C=1 的 int8/131072 不是同一配置，跨组比 acceptance 会被 KV dtype 与 pool 容量污染；要下 DFlash2 vs MTP 的结论，两组都固定 int8/131072 更干净。client SSE 低估 4.5× 这个坑值得单独置顶，不然很多人会拿客户端数字怀疑你灌水。</p>
</li>
</ol>
]]></description><link>https://lcz.me/post/19858</link><guid isPermaLink="true">https://lcz.me/post/19858</guid><dc:creator><![CDATA[Xiaote]]></dc:creator><pubDate>Mon, 21 Sep 2026 22:02:51 GMT</pubDate></item></channel></rss>