<?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[# 双 RX 7900 XTX 跑 FlashNext，ESXi 直通、近 256K 同机实测和空闲功耗]]></title><description><![CDATA[<h1>双 RX 7900 XTX 跑 FlashNext，ESXi 直通、近 256K 同机实测和空闲功耗</h1>
<p dir="auto">搭这台机器的时候，我想的是把长文、代码和图片都放在本地跑。比如一整份材料发进去，最后问的可能是开头提过的一件小事。我想让模型读完以后，还能把那个细节找出来。材料一长，上下文很快就到了二十多万 token。平时我也想让服务一直开着，想用就直接发请求。</p>
<p dir="auto">两张 7900 XTX 就是拿来分担同一个请求的。每张卡有 24 GiB，FlashNext 的 Q5 权重比两张卡加起来还大，长上下文的缓存也要占显存。我的做法是让两张卡分层跑，常用的专家权重尽量留在显存里，其余主要放内存。后来翻日志，Q5 那轮光是驻留在系统内存里的专家权重就有约 78 GiB。虚拟机里约 108 GiB 的内存，这里实实在在用上了。</p>
<h2>两张卡是怎么接的</h2>
<p dir="auto">主机跑 ESXi，两张卡都直通给同一台 Ubuntu 虚拟机。启动后我先确认 ROCm 能认到两张卡，再去看引擎日志，确认两边各跑 24 层，也都分到了专家权重。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>项目</th>
<th>这台机器</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU / 虚拟机</td>
<td>AMD EPYC 7K62，客机分配 50 vCPU，<code>MemTotal</code> 108.06 GiB</td>
</tr>
<tr>
<td>显卡</td>
<td>2 × RX 7900 XTX，<code>gfx1100</code>，各 24 GiB，ESXi PCIe 直通</td>
</tr>
<tr>
<td>系统 / 推理栈</td>
<td>Ubuntu 24.04，ROCm 7.2.1，Strata 引擎 0.1.56，HIP <code>gfx1100</code> 构建</td>
</tr>
<tr>
<td>引擎版本</td>
<td>源码 <code>ffa80d73133ea5c548aefe57889a16e8085a938f</code>，构建开启 <code>STRATA_MMQ_KQUANTS</code></td>
</tr>
<tr>
<td>双卡用法</td>
<td><code>gpu=[0,1]</code>，<code>layer_split="24"</code>，<code>parallel=1</code>，<code>STRATA_PEER_TRANSPORT=host</code></td>
</tr>
</tbody>
</table>
<p dir="auto">客机里看 PCIe，两个设备都报 <code>5.0 GT/s ×32</code>。我把这项当客机枚举信息记录。物理槽位速率、主机侧 NUMA 和卡间带宽，要用宿主机上的测量回答。这轮比较固定的是两张卡分层和 <code>host</code> 传输。</p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/2642a99f-9506-43a8-9d19-3495d7a50f21.png" alt="01-双卡配置.png" class="img-markdown  img-fluid" /></p>
<p dir="auto"><em>图 1　两张 7900 XTX 直通给同一台虚拟机，分担一个推理请求。</em></p>
<p dir="auto">后来做 Q5 对照的时候，我从加载模型开始就记显存，长请求和 102 道题也算在内。用原专家表那轮，两张卡采到的最低剩余显存分别是约 1,363 MiB 和 1,078 MiB。两张都是 24 GiB，跑起来剩的余量也有差别。我后来就分开记两张卡，看哪张先吃紧。</p>
<p dir="auto">最早也试过 Q6，能跑完当时的 17 项，近 256K 长文的首字中位数却到了 438 秒。七分多钟才开始回，日常用实在等不住，我就接着试 Q5。早期的测试参数后来有调整，这个数就当是当时的折腾记录。下面的 Q4 和 Q5 是换成同一批请求以后重新测的。</p>
<h2>请求都跑完了，怎么还有 200 多瓦？</h2>
<p dir="auto">先贴两张 11 月 4 日的监控原图。第一张看着像一半负载，后来再看，GPU 那个圈已经红到 100% 了。显存两张都显示 95%，功耗却从 138 W 跳到了 224 W。</p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/11dacd3d-bb3f-405d-91f4-44cd58ace5d7.png" alt="02-监控-GPU50 (1).png" class="img-markdown  img-fluid" /></p>
<p dir="auto"><em>图 2　监控面板显示 GPU 50%，双卡功耗 138 W。</em></p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/b41ccaa5-541d-4057-a865-6e22905759b7.png" alt="03-监控-GPU100 (1).png" class="img-markdown  img-fluid" /></p>
<p dir="auto"><em>图 3　同一天的另一张截图，GPU 100%，双卡功耗 224 W。</em></p>
<p dir="auto">我先去看是不是还有请求在跑。队列已经空了，输出 token 计数也没再动，可两张卡还是都报 100%，连续 9 次采样的合计功耗在 220–233 W。面板上的 GPU 百分比是两张卡的平均值。之前看到 50% 的时候，拆开查就有过一张 0%、另一张 100% 的情况。停掉推理服务做对照，两个忙碌读数都降到了 0%。</p>
<p dir="auto">后来改 HIP 硬件队列上限逐档试。原来的 <code>GPU_MAX_HW_QUEUES=8</code>，Q5 载入等请求时双卡功耗中位数 227 W，停服务、模型卸载后是 23 W。六队列和五队列在载入后都出现过一张卡报 100%，双卡合计 99 W。四队列跑完短请求看着正常，等 102 题跑完，队列清空后又出现单卡 100%，合计 109.5 W。</p>
<p dir="auto">三队列那轮，我在同一个进程里跑完 102 题和 17 项功能、性能检查，分别测到 23 W 和 25 W，两卡忙碌读数都是 0%。把 <code>GPU_MAX_HW_QUEUES=3</code> 用到正式服务，做完 API 验证后模型还在显存里，双卡合计 19.5 W。这里的瓦数来自显卡遥测，按两张卡相加。</p>
<p dir="auto"><img src="https://upload.lcz.me/uploads/bca914cc-f70f-4986-b6c0-c2cfc5db8ed5.png" alt="04-空闲功耗.png" class="img-markdown  img-fluid" /></p>
<p dir="auto"><em>图 4　每行是对应状态下 6 次采样的中位数，功耗按两张显卡合计。</em></p>
<p dir="auto">四队列那次最容易看走眼，刚启动的十几瓦只说明当时那一小段正常。后面我用三个窗口分别看短请求、102 题、整套长文和图片跑完后的状态。这个结果是我这台 ESXi 直通机配 ROCm 7.2.1 的实测，具体是哪条队列卡住，目前只查到现象跟推理进程的存续一起变化。</p>
<h2>Q4 和 Q5 各要等多久</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>版本</th>
<th>权重来源</th>
<th style="text-align:right">主 GGUF 合计</th>
</tr>
</thead>
<tbody>
<tr>
<td>Q4</td>
<td><code>cygnal/Qwen3.8-Flash-Next-Heretic2-IQ4XS-NGQ4-GGUF</code>，revision <code>b7a350f3ba4c29962cefe9e69af225f142cda9bc</code></td>
<td style="text-align:right">98,403,355,776 B，约 91.65 GiB</td>
</tr>
<tr>
<td>Q5</td>
<td><code>orcarouter/Qwen3.8-Flash-Next-Uncensored-GGUF</code> 的 <code>Q5_K_M</code>，revision <code>98eb3357afd966c25cadd082c7f830eafde7aa52</code></td>
<td style="text-align:right">134,106,668,160 B，约 124.90 GiB，三片</td>
</tr>
</tbody>
</table>
<p dir="auto">体积只算主 GGUF。两边实际使用各自的权重、pack、tokenizer 路径和视觉投影，推理引擎、环境变量和采样对齐。Q4 名字里有 IQ4XS，实际 GGUF 的专家张量混用了 <code>IQ4_XS</code>、<code>IQ4_NL</code> 和 <code>Q5_1</code>，内置 PLE 表为 <code>Q4_0</code>。这是两套可用权重的同机对照，分数差包括了各自的权重版本。</p>
<p dir="auto">两边都用 INT8 KV、<code>--kv-resident 32768</code>、MTP4、双窗口、<code>--expert-cache auto</code> 和 <code>--vram-reserve-mib 1536</code>。262,144 是上下文上限，32,768 是配置的显存 KV 驻留窗口。双窗口是引擎的流水线设置，服务并发仍是 1。每边先跑同一条预热请求，再按 128K 长文一条、近 256K 长文三条、近 256K 代码三条的顺序跑。两边八条实际请求的 payload SHA 都逐条一致。</p>
<p dir="auto">测速请求关闭 thinking，<code>temperature=0.2</code>、<code>top_p=0.95</code>、<code>top_k=20</code>、<code>seed=42</code>。正式计时的七条每条生成 256 token，实际 <code>cache_n=0</code>。PP 和 TG 取引擎的 <code>prompt_per_second</code> 和 <code>predicted_per_second</code>，TTFT 取客户端从发请求到第一个可见字符的时间。128K 实际输入 131,072 token，近 256K 实际输入 261,632 token，近 256K 两组各取三次中位数。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>请求</th>
<th style="text-align:right">读入 Q4 / Q5（tok/s）</th>
<th style="text-align:right">生成 Q4 / Q5（tok/s）</th>
<th style="text-align:right">首字 Q4 / Q5（秒）</th>
</tr>
</thead>
<tbody>
<tr>
<td>128K 长文</td>
<td style="text-align:right">2210.4 / 1447.5</td>
<td style="text-align:right">53.8 / 36.2</td>
<td style="text-align:right">59.53 / 90.82</td>
</tr>
<tr>
<td>近 256K 长文</td>
<td style="text-align:right">2537.5 / 1848.5</td>
<td style="text-align:right">63.2 / 45.6</td>
<td style="text-align:right">103.45 / 141.84</td>
</tr>
<tr>
<td>近 256K 代码</td>
<td style="text-align:right">2568.6 / 1917.1</td>
<td style="text-align:right">65.7 / 53.3</td>
<td style="text-align:right">102.17 / 136.78</td>
</tr>
</tbody>
</table>
<p dir="auto">近 256K 长文三次的生成区间是 Q4 <strong>60.6–68.3</strong>、Q5 <strong>42.7–49.0 tok/s</strong>。长请求里埋了要找回的细节，这几轮两边都找到了。Q5 的长文 TTFT 中位数约 142 秒，后面 256 个 token 的生成阶段约 6 秒。拿它查一份长材料里的某个细节，前面这段等候才是我每天能感觉到的差别。</p>
<h2>日志里能看到的差别</h2>
<p dir="auto">两套都用 <code>--expert-cache auto</code>，实际装进 GPU 的专家数差得不少。我把这轮七条长请求的日志对齐后，单独摘了几项。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>日志字段</th>
<th style="text-align:right">Heretic2 Q4</th>
<th style="text-align:right">OrcaRouter Q5</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPU 常驻专家槽位，两卡合计</td>
<td style="text-align:right">13,459</td>
<td style="text-align:right">9,379</td>
</tr>
<tr>
<td>RAM 中的专家补集</td>
<td style="text-align:right">52.53 GiB</td>
<td style="text-align:right">78.18 GiB</td>
</tr>
<tr>
<td>128K 长文 GPU 专家命中率，单次</td>
<td style="text-align:right">88.5%</td>
<td style="text-align:right">73.8%</td>
</tr>
<tr>
<td>128K 长文 decode 文件等待，单次</td>
<td style="text-align:right">34.5 ms</td>
<td style="text-align:right">1,443.0 ms</td>
</tr>
<tr>
<td>近 256K 长文 GPU 专家命中率，三次中位</td>
<td style="text-align:right">95.4%</td>
<td style="text-align:right">89.0%</td>
</tr>
<tr>
<td>近 256K 长文 KV 显存命中率，三次中位</td>
<td style="text-align:right">96.26%</td>
<td style="text-align:right">96.15%</td>
</tr>
<tr>
<td>近 256K 长文 decode 文件等待，三次中位</td>
<td style="text-align:right">1.1 ms</td>
<td style="text-align:right">97.8 ms</td>
</tr>
<tr>
<td>MTP 草稿接受／提议，七条合计</td>
<td style="text-align:right">876 / 1,260（69.5%）</td>
<td style="text-align:right">850 / 1,296（65.6%）</td>
</tr>
</tbody>
</table>
<p dir="auto">这轮 Q4 的专家权重整体更小，自动分配多塞进了 4,080 个 GPU 槽位。近 256K 长文的 KV 显存命中都在 96% 左右，专家命中率拉开的差距更大，MTP 接受率也有变化。要把那几十秒的首字差距拆到专家搬运、PLE、内核和其他开销，还得另做逐项对照。上面的文件等待是 decode 日志里的字段，和整条请求耗时分开看。</p>
<h2>跑得更快，为什么我还是留了 Q5</h2>
<p dir="auto">同一套冻结 102 题，Q4 答对 <strong>69 题</strong>，Q5 答对 <strong>78 题</strong>。同题集、同评分器、同采样，两边请求失败和 GPU 视觉协议失败都是 0。按类别看是下面这组。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>题类</th>
<th style="text-align:right">Q4</th>
<th style="text-align:right">Q5</th>
</tr>
</thead>
<tbody>
<tr>
<td>中文</td>
<td style="text-align:right">16/20</td>
<td style="text-align:right">18/20</td>
</tr>
<tr>
<td>推理</td>
<td style="text-align:right">8/20</td>
<td style="text-align:right">10/20</td>
</tr>
<tr>
<td>代码</td>
<td style="text-align:right">10/20</td>
<td style="text-align:right">11/20</td>
</tr>
<tr>
<td>金融</td>
<td style="text-align:right">19/24</td>
<td style="text-align:right">21/24</td>
</tr>
<tr>
<td>普通读图</td>
<td style="text-align:right">10/12</td>
<td style="text-align:right">12/12</td>
</tr>
<tr>
<td>文档读图</td>
<td style="text-align:right">6/6</td>
<td style="text-align:right">6/6</td>
</tr>
</tbody>
</table>
<p dir="auto">我翻了几道原始答案。有一道图表题，Q4 连最大项的数值都读对了，可题目要的是类别名，它把数字填了进去。还有一道代码题，题目要单参数的函数，Q4 生成了两个参数，照原来的函数签名调用就对不上。也有像有效税率这种算错的。两份权重版本本身有差异，我就按这两个版本在这套题上的结果选。</p>
<p dir="auto">中间还试过给 Q5 重排专家，把预跑时更常用的那批提前放进显存。我的想法很简单，常用的先放进去，看看能不能少等几次从内存拿数据。两组的显存预留相同，两张卡的专家槽位分别是 4,616 和 4,763，只换初始排序。</p>
<p dir="auto">这轮还踩了个测速坑。请求里已经写了 <code>cache_prompt=false</code>，但第一次重发同一份 128K 提示，日志显示还是复用了 131,065 个 token，实际只重新读了 7 个。那条结果作废。重跑时在隔离测速请求加 <code>strata_checkpoint=false</code>，实测每条 <code>cache_n=0</code>。两边都先预热引擎，再完整读入长提示。</p>
<p dir="auto">同一次 Q5 对照里，预热后 128K 生成从 45.7 掉到 38.8 tok/s，近 256K 长文从 48.0 掉到 41.7 tok/s。近 256K 代码那条冷测生成从 36.4 升到 40.0 tok/s，首字从 163.92 秒拉到 230.45 秒，预热后生成也从 51.1 降到 50.0 tok/s。102 题从 78 到 77，唯一变化是 <code>code-histogram</code> 由对变错。我测完就回到原专家表了。</p>
<p dir="auto">现在机器就常驻 Q5。长材料发一次要等多久，我心里大致有数。对我自己的用法来说，多等这几十秒还能接受。答完以后两张卡能降下来，服务还能继续挂着，这台机子才算顺手。</p>
<h2>跑分时用的参数</h2>
<p dir="auto">两套对照共用的 Strata <code>args</code> 如下。模型、投影、pack 和词表路径按安装位置填。GPU 列表、<code>layer_split</code> 和 <code>parallel</code> 在服务配置里设置，值见上面的硬件表。</p>
<pre><code class="language-bash">--pack "$PACK_DIR" --native "$MODEL_GGUF" --ple-gguf "$PLE_GGUF" \
--expert-profile "$EXPERT_PROFILE" --expert-cache auto --resident-experts \
--prefill auto --pool-workers 24 --max-context 262144 \
--kv int8 --kv-resident 32768 --ple-io direct --ple-row-cache 1048576 \
--spec 4 --spec-min-p 0.65 --mtp "$MTP_DIR" --mtp-q4 all \
--mtp-window 4096 --mtp-draft-vocab "$DRAFT_VOCAB" --pipeline-windows 2 \
--pcie-frac 0 --vram-reserve-mib 1536 --adapt-every 24 \
--prompt-cache 8 --prompt-cache-every 8192 \
--conversation-cache-slots 2 --conversation-cache-mib 8192 \
--conversation-cache-min-tokens 4096 --stats --vision
</code></pre>
<p dir="auto">两边共用的引擎环境变量也贴在这里。调优表的文件名保留，路径按本机填写。</p>
<pre><code class="language-bash">export STRATA_RDNA_MMVQ_WAVE=3
export STRATA_PEER_TRANSPORT=host
export STRATA_HIP_WMMA=1
export STRATA_SELECT_WMMA=1
export STRATA_PREFILL_CPU_SHARE=auto
export STRATA_HC_Q8=0
export STRATA_PREFILL_AUTO_MAX=32768
export STRATA_PREFILL_RING=384
export STRATA_ROUTE_TAIL_SKIP=0
export STRATA_HEAD_MIX_MULTI=1
export STRATA_ONE_TOKEN_COMMIT=0
export STRATA_AUX_CPUS=off
export STRATA_GR_RDNA=1
export STRATA_GR_RDNA_Q8=1
export STRATA_RDNA_Q6K=1
export STRATA_GR_RDNA_A8=1
export GPU_MAX_HW_QUEUES=3
export STRATA_QFUSE=1
export STRATA_COMBINE_CPU=1
export STRATA_VERIFY_QDEDUP=1
export STRATA_WAIT_FUSE=1
export STRATA_ROUTE_PUBLISH=1
export STRATA_GR_INJ_UP=1
export STRATA_GR_FUSED=1
export STRATA_GDN_HEAD=1
export STRATA_GDN_CHUNKED=1
export STRATA_GDN_CHUNK_GDV=0
export STRATA_PA_KLDS=1
export STRATA_GDN_PP=1
export STRATA_GDN_CONVL2=1
export STRATA_GDN_NOY=1
export STRATA_PF_GEMM=1
export STRATA_PF_PAD=1
export STRATA_HC_UPMIX=1
export STRATA_PF_HCDOWN=1
export STRATA_FUSE_CONVAB=1
export STRATA_PA_FAST=1
export STRATA_PF_GROUP_RES=1
export STRATA_KV_HOST_DMA=1
export STRATA_DP_PREFILL=0
export STRATA_RESIDENT_PIN=0
export STRATA_RESIDENT_HEADROOM_GIB=12
export STRATA_HIPBLASLT_TUNING="$HIPBLASLT_TABLE"  # gfx1100-hipblaslt-100202.txt
export STRATA_MTP_STAGE0=0
export HSA_USERPTR_FOR_PAGED_MEM=0
export STRATA_PF_FUSED=1
export STRATA_PF_FUSED_KQ=1
export STRATA_PF_F16=1
</code></pre>
]]></description><link>https://lcz.me/topic/2942</link><generator>RSS for Node</generator><lastBuildDate>Mon, 09 Nov 2026 14:44:15 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/2942.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 09 Nov 2026 12:37:15 GMT</pubDate><ttl>60</ttl></channel></rss>