<?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[T7910 双卡本地推理全记录：7900 XTX 把两张卡跑掉总线，换 R9700 后大 BAR + P2P 一次打通，最后 4 并发 × 160K 稳跑]]></title><description><![CDATA[<h1>T7910 双卡本地推理全记录：7900 XTX 把两张卡跑掉总线，换 R9700 后大 BAR + P2P 一次打通，最后 4 并发 × 160K 稳跑</h1>
<blockquote>
<p dir="auto">平台：Dell Precision Tower 7910（双路 Xeon E5-2683 v4 / 128 GB ECC / 无 BMC）<br />
本文是<strong>合集</strong>：前一半是先前那篇《双 7900 XTX 经 PCIe 交换机跑社区魔改 SGLang：TP=2 真跑到 100 tok/s，但一条卡死的请求把两张卡打掉了总线》的压缩版，后一半是<strong>换卡之后的新进展</strong>。没看过前篇的可以直接从这篇读起。<br />
两阶段的实测数字<strong>不能直接互相比较</strong>（模型、量化档、框架、prompt 全变了），所以本文只比「形态与能力」，速度账单独列表并标口径。</p>
</blockquote>
<hr />
<h2>TL;DR</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th></th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> <strong>第一阶段（2× 7900 XTX 经 PCIe 交换机 / SGLang + W4A16 GPTQ + EAGLE MTP-3）</strong></td>
<td>TP=2 真跑起来了：<strong>100 tok/s、MTP 接受率 0.86、KV 243,403 token @196K</strong> —— 速度达标</td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> <strong>但服务在第 6 分钟被一条卡死的请求把两张卡打成 <code>device lost from bus</code></strong></td>
<td>驱动连试 <strong>8 次复位全部失败</strong>（<code>ret = -19</code>），只能重启救回。<strong>根因不是供电也不是过热</strong>（掉卡瞬间仅 272 W / 54 °C）</td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/26a0.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--warning" style="height:23px;width:auto;vertical-align:middle" title="⚠" alt="⚠" />️ <strong>那代卡的死结</strong></td>
<td>7900 的 <strong>VBIOS 开机只申请 256 MB BAR</strong> ⇒ 这台机器拿不到大 BAR、也就<strong>没有 P2P</strong>；跨卡通信退回 NCCL，且经交换机有 <strong>4 跳只跑 Gen2</strong> ⇒ 一切能跑，但脆弱</td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f504.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--arrows_counterclockwise" style="height:23px;width:auto;vertical-align:middle" title="🔄" alt="🔄" /> <strong>转折（本文主线）</strong></td>
<td>换成 <strong>2× R9700 32 GB，插 CPU 直连槽</strong>，再给自编内核的 <code>p2pdma</code> 白名单加一行 ⇒ <strong>大 BAR 32 GB 与 P2P 一次打通</strong>，卡间带宽 <strong>2.39 → 10.26 GB/s</strong></td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> <strong>现在的形态</strong></td>
<td>双卡 TP=2 + MXFP4 量化 + <strong>4 并发 × 单请求 163,840 上下文</strong>（KV <strong>717,986 token / 4.38×</strong>），单流贪心 <strong>43 tok/s</strong>，已连续稳定运行（含重启在内 0 次掉卡）</td>
</tr>
<tr>
<td>🧱 <strong>本轮撞的三面新墙</strong></td>
<td>① <strong>掉电写坏编译缓存</strong> ⇒ 容器每 4~5 分钟<strong>静默死</strong>；② <strong>OD 偏移（时钟）才是速度旋钮</strong>；③ <strong>160K 的代价 100% 落在投机接受长度上</strong></td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f511.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--key" style="height:23px;width:auto;vertical-align:middle" title="🔑" alt="🔑" /> <strong>最反直觉的一条</strong></td>
<td><strong>投机解码的接受率与「精度」无关</strong> —— 草稿 token 一律经主模型验证，接受率掉只掉速度；决定精度的是<strong>主模型量化档 + 上下文长度</strong>。别为了接受率去牺牲别的东西</td>
</tr>
<tr>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f3af.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--dart" style="height:23px;width:auto;vertical-align:middle" title="🎯" alt="🎯" /> <strong>结论</strong></td>
<td>这台机器上「大 BAR / P2P」不是玄学，是<strong>卡 VBIOS 的申请尺寸</strong>决定的。同一台 T7910，换一代卡就能从「能跑但会掉」变成「稳跑」——而你不需要换主板、不需要刷 UEFI</td>
</tr>
</tbody>
</table>
<hr />
<h2>一、平台与环境</h2>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>项</th>
<th>值</th>
</tr>
</thead>
<tbody>
<tr>
<td>主机</td>
<td>Dell Precision Tower 7910，双路 Xeon E5-2683 v4 @2.10 GHz（16C/32T×2），128 GB DDR4 ECC，BIOS A34（2020）</td>
</tr>
<tr>
<td>系统</td>
<td>Ubuntu 24.04；<strong>自编内核 7.0.14-p2pdma</strong>（含下文那 4 行白名单补丁）；Secure Boot 关；<code>CONFIG_PCI_P2PDMA=y</code></td>
</tr>
<tr>
<td>供电</td>
<td>显卡与 PCIe 交换机走<strong>独立一台 1500 W 电源</strong>（与主机电源分开）；主机侧另有外接显卡供电</td>
</tr>
<tr>
<td>阶段一显卡</td>
<td>2× RX 7900 XTX 24 GB（RDNA3 / gfx1100），挂在 <strong>PLX88096 PCIe 交换机</strong>下游</td>
</tr>
<tr>
<td>阶段二显卡</td>
<td>2× <strong>AMD Radeon AI PRO R9700 32 GB</strong>（RDNA4 / gfx1201），插 <strong>CPU 直连槽</strong>（不经交换机）</td>
</tr>
<tr>
<td>阶段一软件栈</td>
<td>ROCm 7.2 + <code>StevenChenSE/sglang</code> @ <code>gfx1100-support</code> 分支 + Qwen3.8-27B W4A16 AutoRound GPTQ</td>
</tr>
<tr>
<td>阶段二软件栈</td>
<td>社区容器 <code>magiccodingman/vllm-radiance</code>（版本串 <code>0.9.3-dev.vllm0.28.0-r4d0.5.0-mxfp4.rx4.dflash2…</code>）+ Qwen3.8-27B <strong>MXFP4</strong>（AWQ/Quark 量化）</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>两种形态对照</strong>（这才是本文的主线）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th></th>
<th>阶段一：7900 XTX + PCIe 交换机</th>
<th>阶段二：R9700 + CPU 直连槽</th>
</tr>
</thead>
<tbody>
<tr>
<td>大 BAR（Resizable BAR）</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> <strong>256 MB</strong>（<code>lspci</code> 支持列表里明明有 32 G，VBIOS 只申请 256 M）</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> <strong>32768 MB</strong>（启动日志 <code>Detected VRAM RAM=32624M, BAR=32768M</code>）</td>
</tr>
<tr>
<td>P2P（卡对卡）</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> <code>canAccessPeer</code> 双向 = 0</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> <code>P2P access : ENABLED 0↔1</code></td>
</tr>
<tr>
<td>卡间链路</td>
<td>交换机下游 <strong>4 跳只跑 Gen2（5 GT/s）</strong></td>
<td>根口 <strong>Gen4 x16</strong></td>
</tr>
<tr>
<td>跨卡通信方式</td>
<td>退回 NCCL（自带的 PCIe All-Reduce 在 gfx1100 上编不过）</td>
<td>容器自带的 <strong>R4D one-shot all-reduce（走 P2P，byte-identical to RCCL）</strong></td>
</tr>
<tr>
<td>卡间实测带宽</td>
<td><strong>2.39 GB/s</strong>（all-reduce 峰值，延迟主导）</td>
<td><strong>10.26 GB/s 单向 / 19.71 GB/s 双向</strong></td>
</tr>
<tr>
<td>结果</td>
<td>100 tok/s，但<strong>第 6 分钟掉总线</strong></td>
<td>长跑稳定，4 并发 × 160K</td>
</tr>
</tbody>
</table>
<hr />
<h2>二、第一阶段（压缩版）：能跑，但会掉</h2>
<h3>2.1 部署踩坑（严格按我踩到的顺序，压成一张表）</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>#</th>
<th>坑</th>
<th>症状</th>
<th>解法</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>魔改 fork 的依赖版本</td>
<td>README 说 torch 2.11，<code>pyproject.toml</code> 实际要 2.13</td>
<td><strong>以 pyproject 为准</strong>，并用 constraints 锁死 ROCm 版 torch，否则依赖解析会把它换成 CUDA 版</td>
</tr>
<tr>
<td>2</td>
<td>torchvision 装成 CUDA 版</td>
<td><code>import sglang</code> 直接炸：<code>operator torchvision::nms does not exist</code></td>
<td>从 ROCm 源装 <code>torchvision==0.26.0+rocm7.2 --no-deps</code></td>
</tr>
<tr>
<td>3</td>
<td>（最阴）PyPI 的 <code>sglang-kernel</code> 会盖住 fork 的 <strong>Python 层</strong></td>
<td>调度器初始化阶段挂：<code>gptq_gemm() takes 7 positional arguments but 8 were given</code></td>
<td><strong>只覆盖 <code>.so</code> 不够，Python 层也要整层覆盖</strong></td>
</tr>
<tr>
<td>4</td>
<td><code>kernels</code> 包与新版 transformers 冲突</td>
<td><code>ValueError: Either a revision or a version must be specified.</code></td>
<td><code>pip uninstall kernels</code>（该代码在 <code>try/except ImportError</code> 里）</td>
</tr>
<tr>
<td>5</td>
<td>AOT 内核编译</td>
<td>——</td>
<td>唯一一步「照着走就行」：<code>setup_rocm.py build_ext --inplace</code>（本机双路 E7 单核弱，<strong>约 50 分钟</strong>）</td>
</tr>
<tr>
<td>6</td>
<td>fork 自带的自定义 All-Reduce 编不过</td>
<td><code>__builtin_amdgcn_global_store_b128 needs target feature gfx940-insts</code>（那是 MI300/CDNA3 专属指令）</td>
<td>关掉，退回标准 RCCL：<code>SGLANG_RDNA_CUSTOM_AR=0</code></td>
</tr>
<tr>
<td>7</td>
<td><strong>（致命）无 P2P ⇒ 所有 symm-mem / multimem 路径都崩</strong></td>
<td>一进 decode 就 SIGABRT，栈顶却是 <code>logits_processor.py</code>（极具误导性，实际炸在 <strong>decode CUDA Graph 捕获阶段</strong>建通信器时）</td>
<td>① <code>--disable-custom-all-reduce</code> + <code>SGLANG_OPT_USE_CUSTOM_ALL_REDUCE_V2=0</code>；② <strong>打补丁</strong>：给 <code>MultimemAllGatherer._build()</code> 首行插 <code>return None</code>，强制走 NCCL all-gather</td>
</tr>
<tr>
<td>8</td>
<td>起服务前没检查谁占着卡</td>
<td>一个 <code>unless-stopped</code> 的旧容器在无限重启，每次抢走一张卡 23.7 GB ⇒ TP=2 直接被挡死</td>
<td><code>rocm-smi --showmeminfo vram</code> + <code>docker inspect</code> 看重启策略</td>
</tr>
</tbody>
</table>
<blockquote>
<p dir="auto">坑 7 的结论值得单独记一句：<strong>无 P2P 的 RDNA3 是能跑 TP 的，前提是把跨卡通信全部退回 NCCL。</strong></p>
</blockquote>
<h3>2.2 成绩单（数字取自引擎自己的日志，不是客户端计时）</h3>
<table class="table table-bordered table-striped">
<caption id="tp2mtp" style="caption-side:bottom">双卡 TP=2 生成速度与 MTP 接受率</caption>
<thead>
<tr>
<th>指标</th>
<th>实测值</th>
</tr>
</thead>
<tbody>
<tr>
<td>生成速度</td>
<td><strong>100.33 tok/s</strong>（稳定段；爬升过程 0.29 → 70.75 → 97.61 → 100.33）</td>
</tr>
<tr>
<td>MTP 接受率</td>
<td><strong>0.86</strong>（每轮 4 个草稿 token 实际接受 3.58 个）</td>
</tr>
<tr>
<td>KV 容量</td>
<td><strong>243,403 token</strong></td>
</tr>
<tr>
<td>上下文</td>
<td>196,608</td>
</tr>
<tr>
<td>显存占用</td>
<td>卡0 22.46 GB / 卡1 22.19 GB（各 24.6 GB 可用）</td>
</tr>
<tr>
<td>权重加载 / 就绪</td>
<td>20 GB 用时约 77 s；服务就绪共 112 s</td>
</tr>
</tbody>
</table>
<p dir="auto"><img src="https://upload.lcz.me/uploads/90f27eec-ae68-452d-bc35-78b290a3dcbb.jpeg" alt="fig2.jpeg" class=" img-fluid img-markdown" /></p>
<blockquote>
<p dir="auto"><strong>口径</strong>：W4A16 量化、<strong>单流</strong>、上下文 196 K、<strong>开启 EAGLE MTP-3</strong>；速度取自引擎日志 <code>decode batch</code> 行（第 405 个 token 之后稳定），非多并发聚合、非客户端计时。<br />
顺带一个教训：这个 100 tok/s 是<strong>强规律 prompt</strong> 下的成绩 —— 换成不可预测的 prompt 会掉到 43 tok/s（见 2.5 的 fig5）。</p>
</blockquote>
<h3>2.3 掉卡全过程</h3>
<p dir="auto">服务 02:55:43 就绪，我发了一条请求做基准 —— <strong>那条请求卡住了</strong>（引擎两个进程各 200% CPU 空转），6 分钟后，卡崩了。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>时间</th>
<th>事件</th>
</tr>
</thead>
<tbody>
<tr>
<td>02:55:43</td>
<td>服务就绪、正常响应请求，速度见上图</td>
</tr>
<tr>
<td><strong>03:01:15</strong></td>
<td><strong>卡0 爆 <code>sq_intr</code> 着色器/队列错误</strong>，紧接着两张卡 <code>device lost from bus!</code></td>
</tr>
<tr>
<td>03:08:52</td>
<td>驱动自救：<code>ring sdma0 timeout</code> → <code>failed to reset legacy queue</code> → <code>GPU reset begin!</code> → <strong><code>GPU reset end with ret = -19</code></strong>，连试 <strong>8 次全失败</strong></td>
</tr>
<tr>
<td>之后</td>
<td><code>SMU: bus error ... 0xFFFFFFFF</code> 无限刷；<code>nvtop</code> / <code>rocm-smi</code> 读不到任何温度</td>
</tr>
<tr>
<td>03:30:48</td>
<td>发出 <code>reboot</code></td>
</tr>
<tr>
<td>03:38</td>
<td>机器起来，两卡 <code>SMU is initialized successfully!</code>、<code>device lost</code> 0 条 ⇒ <strong>卡自己回来了</strong></td>
</tr>
</tbody>
</table>
<p dir="auto">![全过程时间线]<br />
<img src="https://upload.lcz.me/uploads/5a3c5f63-b347-43cd-9197-d2dd189fe256.jpeg" alt="fig3.jpeg" class=" img-fluid img-markdown" /></p>
<h3><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f511.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--key" style="height:23px;width:auto;vertical-align:middle" title="🔑" alt="🔑" /> 转折点</h3>
<p dir="auto"><strong>掉卡前最后一条有效遥测（03:01:14）：卡0 272 W / 56 °C，卡1 271 W / 54 °C，风扇刚爬到 799 / 642 RPM。</strong></p>
<p dir="auto">我最初的条件反射是「又是供电/过热把机器打死了」，但数据说 <strong>272 W 离 355 W 上限还差得远，54 °C 更是凉快</strong>。</p>
<p dir="auto">![掉卡瞬间功耗曲线]<br />
<img src="https://upload.lcz.me/uploads/5c668bb7-651b-4f43-b1c4-d779c7ecc769.jpeg" alt="fig1.jpeg" class=" img-fluid img-markdown" /></p>
<p dir="auto">⇒ 方向立刻从「供电/散热」掰到「<strong>计算故障 + 驱动复位失败</strong>」。</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>#</th>
<th>假设</th>
<th>判据</th>
<th>结论</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>过热</td>
<td>掉卡瞬间 54–56 °C，风扇刚起转</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 排除</td>
</tr>
<tr>
<td>2</td>
<td>供电瞬态尖峰（老毛病）</td>
<td>272/271 W，远低于 355 W cap；主机本身没断电</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 排除</td>
</tr>
<tr>
<td>3</td>
<td>卡物理损坏</td>
<td><code>lspci</code> 仍枚举、驱动仍绑定；<strong>重启后完全恢复</strong></td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 排除</td>
</tr>
<tr>
<td>4</td>
<td>驱动/固件崩 + 复位失败</td>
<td><code>sq_intr</code> → <code>device lost from bus</code> → 8 次 <code>GPU Recovery Failed: -19</code></td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> <strong>就是这个</strong></td>
</tr>
<tr>
<td>5</td>
<td>触发源</td>
<td>时间线：<strong>一条卡死的请求</strong>在同一秒触发 <code>sq_intr</code>；此前 6 分钟完全正常</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> 高置信</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>我自己的责任</strong>：那条请求卡死后我拖了 18 分钟才发现，而崩卡发生在卡死的<strong>第 1 分钟内</strong>。如果 2 分钟内就杀进程，大概率不会有这次掉卡。这条已写成硬规矩：<strong>请求超时 3~5 倍、或引擎 CPU 拉满却没有吞吐日志 ⇒ 立刻杀进程。</strong></p>
<h3>2.4 想拿大 BAR / P2P 的三条路，全走死了</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>路径</th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr>
<td>运行时扩 BAR（<code>resource0_resize</code>）</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 写值返回 <code>I/O error</code>，内核 <code>old value restored</code></td>
</tr>
<tr>
<td>内核参数 <code>pci=realloc</code></td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 大 BAR 仍 256 M，桥窗口仍刷 <code>mem size 0x800200000: failed to assign</code></td>
</tr>
<tr>
<td>把 PLX 交换机从 CPU1 侧挪到 <strong>CPU0</strong> 侧</td>
<td><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> 两卡都变成 <code>numa_node=0</code>、BAR 地址落在 CPU0 的 1 TiB 窗口内，<strong>大 BAR 仍 256 M</strong></td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>机理</strong>：大 BAR 要求「先选 BAR 尺寸、后分配桥窗口」，而<strong>选尺寸的只能是 BIOS/VBIOS</strong>（驱动 probe 时再写 rebar 控制寄存器，桥窗口早已被固件定死）。<br />
⇒ <strong>根因唯一：7900 的 VBIOS 开机只申请 256 M。</strong></p>
<blockquote>
<p dir="auto">附一条容易踩的判据：<strong>别用 amdgpu 日志的「沉默」判断 P2P 可用</strong> —— 大 BAR 那层的否决是静默的。只剩两张 7900 时日志里 P2P 判词是 <strong>0 条</strong>，看着像通过，但 HIP 实测 <code>canAccessPeer</code> 双向都是 0。<strong>判据只能用 <code>hipDeviceCanAccessPeer</code> + 真跑的 <code>hipMemcpyPeer</code>。</strong></p>
</blockquote>
<h3>2.5 附带实测：三张这次才补上的图</h3>
<p dir="auto"><strong>① 并发天花板是 <code>max_running_requests</code>，不是硬件；提高 OD 上限没有收益</strong></p>
<p dir="auto">![并发基准与 OD 上限对照]<br />
<img src="https://upload.lcz.me/uploads/ccf19814-e117-4135-b7fd-ad2e976e003d.jpeg" alt="fig4-并发基准.jpeg" class=" img-fluid img-markdown" /></p>
<ul>
<li>聚合吞吐在<strong>并发 4 撞天花板</strong>（92.4 tok/s），并发 8 不再增长（92.6），只是排队：<strong>TTFT 从 0.6 s 炸到均值 6.0 s / 最大 11.9 s</strong>。</li>
<li>OD 上限 1500 MHz vs 1800 MHz：<strong>两轮核频均值都只有约 1200 MHz、功耗约 108 W</strong> —— 卡根本没顶到上限，所以提上限自然没收益。<strong>这个结论和本文第五章「时钟偏移才是旋钮」是一对</strong>，别混。</li>
</ul>
<p dir="auto"><strong>② 同样的并发，只换 prompt，吞吐差 55% —— 因为 MTP 接受率变了</strong></p>
<p dir="auto">![prompt 类型对接受率与吞吐的影响]<br />
<img src="https://upload.lcz.me/uploads/b698f38a-0c17-46dc-a175-33be4fbef5ca.jpeg" alt="fig5-prompt类型与接受率.jpeg" class=" img-fluid img-markdown" /></p>
<ul>
<li>技术散文 accept rate <strong>0.49</strong> vs 规整字词表 <strong>0.79</strong>：单流吞吐 42.9 → 66.7 tok/s（<strong>+55%</strong>），并发 4 时聚合 92.4 → 122.7。</li>
<li>结论：<strong>接受率是 token 级可预测性的函数</strong>，跟你用不用随机内容无关。这条在第六章会再出现一次，但方向相反 —— 那时我们要说明<strong>接受率不影响精度</strong>。</li>
</ul>
<p dir="auto"><strong>③ 卡间通信：不是带宽不够，是链路太长 + 延迟太高</strong></p>
<p dir="auto">![卡间通信与 PCIe 链路实测]<br />
<img src="https://upload.lcz.me/uploads/b00373c9-05cf-4df6-bb9f-0820c55b7be5.jpeg" alt="fig6-卡间通信.jpeg" class=" img-fluid img-markdown" /></p>
<ul>
<li>all-reduce 峰值带宽仅 <strong>2.39 GB/s</strong>；<strong>1 KB 到 256 KB 的耗时完全一样（约 0.17 ms）</strong> ⇒ 这一段纯粹是<strong>延迟</strong>，不是带宽。</li>
<li>交换机下游<strong>有 4 跳只跑到 Gen2（实跑 5 GT/s，能力 16 GT/s）</strong>。</li>
<li>推算：64 层 × 2 次 all-reduce = 每步约 <strong>128 次集合通信</strong>，每次约 0.17 ms ⇒ <strong>约 22 ms/步</strong>，与实测单流 <strong>23 ms/token</strong> 同量级 ⇒ <strong>通信延迟主导解码</strong>。</li>
</ul>
<blockquote>
<p dir="auto">这三张图的共同结论：<strong>在无 P2P 平台上跑 TP，你付的钱不是带宽，是延迟和脆弱性。</strong></p>
</blockquote>
<h3>2.6 掉卡后的恢复动作（建议背下来）</h3>
<ol>
<li><strong>先看内核日志定性</strong>：<code>journalctl -k | grep -iE 'device lost|sq_intr|Recovery Failed'</code></li>
<li><strong>先试 <code>reboot</code>，但要有耐心</strong>：本次 03:30:48 发出、03:38 才起来（约 7 分钟），期间全网段无响应、很像死机 —— <strong>等满 10 分钟</strong>再判死</li>
<li><strong>还没回来再冷断电</strong>：关机 → 断 PSU 30–60 s → 按顺序上电</li>
<li><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> <strong>绝对不要</strong>用 <code>unbind</code> / <code>bind</code> 或 <code>resource0_resize</code> 去「救」一块 AMD 卡 —— 我曾这么干过一次，直接<strong>内核 NULL 指针 panic + 整机紫屏</strong></li>
</ol>
<hr />
<h2>三、转折：换 R9700，大 BAR 与 P2P 一次打通</h2>
<p dir="auto">拆掉两张 7900 XTX，换成两张 <strong>R9700 32 GB</strong>，插到 <strong>CPU 直连槽</strong>（不经交换机），然后给自编内核打了一行补丁：</p>
<pre><code class="language-c">/* drivers/pci/p2pdma.c —— 让同/跨 Root Port 的 GPU 之间允许 P2P（本机 host bridge 8086:6f00）*/
{PCI_VENDOR_ID_INTEL, 0x6f00, REQ_SAME_HOST_BRIDGE},
</code></pre>
<p dir="auto"><strong>三条判据一次性全绿</strong>（都来自启动日志与 sysfs，不是「感觉能行」）：</p>
<pre><code># ① 大 BAR 拿满
Detected VRAM RAM=32624M, BAR=32768M          （两卡各 32 GiB）

# ② 可见显存 == 实际显存（大 BAR 生效的零成本判据）
card0  34208743424 / vis=34208743424
card1  34208743424 / vis=34208743424

# ③ P2P 打开 + 容器自己的互访矩阵全 1
P2P access : ENABLED   0↔1
rocm-bandwidth-test: Inter-Device Access（2 号/3 号设备之间 = 1）
RADIANCE_USE_R4D_AR   P2P one-shot all-reduce for TP=2, byte-identical to RCCL
</code></pre>
<p dir="auto"><strong>卡间带宽对照</strong>（阶段一 2.39 GB/s 是 all-reduce 峰值；阶段二为卡对卡拷贝）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th></th>
<th>阶段一 7900 XTX（经交换机、无 P2P）</th>
<th>阶段二 R9700（直连、开 P2P）</th>
</tr>
</thead>
<tbody>
<tr>
<td>卡间有效带宽</td>
<td>2.39 GB/s</td>
<td><strong>10.26 GB/s 单向 / 19.71 GB/s 双向</strong></td>
</tr>
<tr>
<td>每次通信的固定延迟</td>
<td>约 <strong>0.17 ms</strong>（1 KB~256 KB 都一样）</td>
<td>每次 all-reduce 不再绕 NCCL，走 P2P one-shot</td>
</tr>
</tbody>
</table>
<p dir="auto">![换卡前后：大 BAR / P2P / 卡间带宽对照]<br />
<img src="https://upload.lcz.me/uploads/3fe481fb-e20d-426e-833d-963f08225ec8.jpeg" alt="fig7.jpeg" class=" img-fluid img-markdown" /></p>
<blockquote>
<p dir="auto"><strong>但请注意最后那个 0.2~0.4%</strong>：这套部署里<strong>每步跨卡通信量只有 1.3~2.6 MB</strong>，跟 10.26 GB/s 比只占千分之几。⇒ <strong>P2P 的价值不在带宽，在于「能开起来」和「延迟低、路径短」</strong>。反过来说：如果你为了带宽去折腾 P2P，方向就错了。</p>
</blockquote>
<p dir="auto"><strong>换卡后立刻要做的三件事</strong>（顺序别反）：</p>
<ol>
<li>先 <code>unbind</code> 旧卡 → 断电 → 插新卡（<strong>动卡必须断电</strong>）</li>
<li>先给交换机/显卡那台独立电源通电，<strong>再开主机</strong></li>
<li>起来先验上面三条判据，<strong>再</strong>去装模型服务 —— 判据不过就别浪费时间调服务</li>
</ol>
<hr />
<h2>四、新墙 ①：掉电写坏编译缓存 ⇒ 容器每 4~5 分钟静默死</h2>
<p dir="auto">这是本轮最阴的一个坑，也是最值得抄走的排查路径。</p>
<h3>症状</h3>
<p dir="auto">服务<strong>跑起来又自己死</strong>，而且非常规律：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>时间</th>
<th>事件</th>
</tr>
</thead>
<tbody>
<tr>
<td>22:41:50</td>
<td>主机<strong>硬掉电</strong>（日志戛然而止，无 shutdown / Oops / panic —— 上电后对比新 boot 才确认是掉电）</td>
</tr>
<tr>
<td>22:45</td>
<td>冷启动回来（此时 160K 的 KV 分配<strong>已经成功过</strong>：<code>731,270 tokens, 4.46x @163,840</code>）</td>
</tr>
<tr>
<td>22:50:34</td>
<td>容器起后 <strong>约 4 分钟死亡</strong>（<code>RestartCount 1</code>）</td>
</tr>
<tr>
<td>22:54:24</td>
<td>再起，<strong>又是 4 分钟死亡</strong>（<code>RestartCount 2</code>）</td>
</tr>
<tr>
<td>——</td>
<td><code>docker inspect</code>：<strong>退出码 0、OOMKilled=false、内核无 amdgpu 报错、entrypoint 里没有看门狗</strong></td>
</tr>
<tr>
<td>22:57</td>
<td>隔离整套编译缓存</td>
</tr>
<tr>
<td>23:02 → 23:11</td>
<td>从零重编，<strong>一次通过、再没崩过</strong></td>
</tr>
</tbody>
</table>
<h3>三条判据（缺一条都会误判）</h3>
<ol>
<li><strong>它不是随机掉电</strong>：两轮死亡时间几乎相同（4 分钟），稳定复现 ⇒ 确定性病因</li>
<li><strong>它不是被杀的</strong>：退出码 0 + 无 OOM + 内核无告警 ⇒ 进程是「自己走完」的</li>
<li><strong>日志停在 CUDA Graph 捕获阶段</strong>，但显存监控证明权重已经加载到 29.4 GB ⇒ <strong>日志随进程缓冲一起丢了</strong>，别拿日志位置当「没跑起来」的证据</li>
</ol>
<h3>真凶判据（一条命令）</h3>
<pre><code class="language-bash"># 数「掉电瞬间正在被写」的缓存文件
find &lt;缓存目录&gt; -path '*BROKEN*' -prune -o -type f -size 0 ! -name '*.lock' -print | wc -l
# 隔离前 = 49（全是 torch_aot_compile/*/inductor_cache/ 下的 .py / *.best_config）
# 隔离后 = 0
</code></pre>
<p dir="auto"><strong>因果链</strong>：掉电砸在<strong>首次编译</strong>过程中（掉电窗口里 40 个文件正在写）⇒ 留下 49 个 0 字节缓存文件 ⇒ 之后每次启动都读这份坏缓存，<strong>在 cudagraph 捕获阶段稳定死</strong>。<br />
（为什么是首次编译？因为我把 <code>max_model_len</code> 改成 163,840 触发了 AOT 缓存从零重建 —— <strong>改这类参数会重编，重编期断电就是灾难</strong>。）</p>
<h3>修法：隔离，不要删</h3>
<pre><code class="language-bash">mv torch_compile_cache{,.BROKEN-&lt;日期&gt;-&lt;原因&gt;}   # 整套改名保留，出问题时还能回查证据
mv triton/&lt;相关键目录&gt;{,.BROKEN-...}             # 同一时刻在写的 triton 键目录一起隔离
# 然后重启服务，让它从零重编
</code></pre>
<p dir="auto">![掉电写坏编译缓存：崩循环与判据]<br />
<img src="https://upload.lcz.me/uploads/2b1c9f63-71de-489b-92ed-37841c9e450c.jpeg" alt="fig8.jpeg" class=" img-fluid img-markdown" /></p>
<blockquote>
<p dir="auto"><strong>收进习惯</strong>：① 只要「服务起得来但总在几分钟内自己死」，先查 0 字节缓存；② 隔离而不是删除，保留证据；③ 编译期尽量别断电（真要改配置，先确认不会触发首编，或者编完再断电）。</p>
</blockquote>
<hr />
<h2>五、新墙 ②：慢的不是功率上限，是<strong>时钟偏移</strong></h2>
<p dir="auto">阶段一那张 fig4 说「<strong>提高 OD 上限</strong>没有收益」，因为它测的是 <strong>cap</strong>。本轮踩的是另一个旋钮：<strong>偏移（offset）</strong>。</p>
<p dir="auto">我把供电护栏从「激进」调到「保守」时，顺手把 SCLK 偏移从 −250 MHz 改成 −500 MHz，结果每步耗时<strong>稳定 +13%</strong>：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>SCLK 偏移</th>
<th>每步耗时（多次实测）</th>
<th>均值</th>
<th>贪心接受长度</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>−500 MHz</strong></td>
<td>75.1 / 75.2 / 75.9 ms</td>
<td><strong>75.4 ms</strong></td>
<td>26.1% / 27.5%</td>
</tr>
<tr>
<td><strong>−250 MHz</strong></td>
<td>65.5 / 65.6 / 66.1 / 66.7 ms</td>
<td><strong>66.0 ms</strong></td>
<td><strong>26.1%</strong>（一丝不动）</td>
</tr>
</tbody>
</table>
<p dir="auto">![SCLK 偏移与每步耗时：纯时钟账]<br />
<img src="https://upload.lcz.me/uploads/0c8dd58d-41a2-4815-8ae0-2f62274aaef6.jpeg" alt="fig9.jpeg" class=" img-fluid img-markdown" /></p>
<p dir="auto"><strong>归因很干净</strong>：只改 SCLK 偏移，<strong>接受长度完全没变</strong>（26.1% 一模一样），而每步耗时掉了 12.5%、速度 +14%。<br />
⇒ 这就是一笔<strong>纯时钟账</strong>，跟「功率上限」「量化档」「上下文」都无关。</p>
<blockquote>
<p dir="auto">判据写法备忘：验证这类偏移有没有真正落到硬件，<strong>不能只看 <code>--show</code> 的回读值</strong>（那只是 sysfs 写回什么就显示什么），要<strong>用「每步耗时」这种端到端指标去反证</strong>。另外，一次性写两张卡的 OD 有竞态，可能存在「只配到一张卡」的情况 —— 回读两张卡各自的节点、并跑一段负载看两卡是否对称。</p>
</blockquote>
<p dir="auto"><strong>现在的供电护栏</strong>（这台机器有过 PSU 瞬态过载史，护栏是必要的）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>项</th>
<th>值</th>
</tr>
</thead>
<tbody>
<tr>
<td>功率上限</td>
<td><strong>230 W</strong>（出厂 300 W，下限 210 / 上限 330）</td>
</tr>
<tr>
<td>SCLK 偏移</td>
<td><strong>−250 MHz</strong></td>
</tr>
<tr>
<td>VDDGFX 偏移</td>
<td><strong>−75 mV</strong></td>
</tr>
<tr>
<td>实测表现</td>
<td>两卡 91~138 W、温度 38~42 °C —— 离上限很远，<strong>慢不是 cap 造成的</strong></td>
</tr>
<tr>
<td>稳定性</td>
<td>本阶段长跑 0 次掉卡；内核侧保留 <code>kernel.panic=15</code>（掉卡能自愈重启）</td>
</tr>
</tbody>
</table>
<hr />
<h2>六、新墙 ③：160K × 4 并发，代价 100% 落在「接受长度」上</h2>
<p dir="auto">服务是社区容器 <code>magiccodingman/vllm-radiance</code>，双卡 TP=2、MXFP4 权重、<strong>4 并发 × 单请求 163,840 上下文</strong>。</p>
<h3>容量账（引擎自己算的）</h3>
<pre><code>GPU KV cache size: 717,986 tokens
Maximum concurrency for 163,840 tokens per request: 4.38x
Available KV cache memory: 14.28 GiB        （权重 ~9.5 GiB/卡，两卡各占 ~30 GB）
</code></pre>
<p dir="auto">⇒ <strong>4 并发 × 160K 是这套组合的上限档</strong>，再想加并发就得牺牲单请求上下文。</p>
<h3>128K → 160K 的完整取舍账（同机、同脚本、单流、贪心）</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th></th>
<th>131,072 上下文</th>
<th><strong>163,840 上下文</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>KV 容量</td>
<td>679,300 token（5.18×）</td>
<td><strong>717,986 token（4.38×）</strong></td>
</tr>
<tr>
<td>每步耗时</td>
<td>63.9 / 64.4 ms（均 64.2）</td>
<td><strong>65.5 / 66.7 ms（均 66.0）</strong>（+2.8%，几乎没变）</td>
</tr>
<tr>
<td>单流吞吐（贪心）</td>
<td>52.45 tok/s</td>
<td><strong>43.02 tok/s</strong>（−18%）</td>
</tr>
<tr>
<td>贪心接受长度</td>
<td>33.6%</td>
<td><strong>26.1%</strong></td>
</tr>
</tbody>
</table>
<p dir="auto">![160K 与 128K 的取舍账]<br />
<img src="https://upload.lcz.me/uploads/0b01a789-a61a-4961-922b-8dbd863d83ff.jpeg" alt="fig10.jpeg" class=" img-fluid img-markdown" /></p>
<p dir="auto"><strong>结论很干脆</strong>：多出 25% 的上下文，<strong>每步耗时几乎不动，代价 100% 落在接受长度上</strong>（33.6% → 26.1%）。<br />
⇒ 想要 52 tok/s 就得回 128K；想要 160K 就是 43 tok/s。<strong>这是一笔纯速度交易，不是质量交易</strong>（见下）。</p>
<h3><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f511.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--key" style="height:23px;width:auto;vertical-align:middle" title="🔑" alt="🔑" /> 概念更正：<strong>接受率与精度无关</strong></h3>
<p dir="auto">这是本轮最该讲清楚的一条，因为论坛上经常被当成一回事：</p>
<ul>
<li><strong>投机解码是无损的</strong>：草稿模型提出的 token，一律要经<strong>主模型验证</strong>才被接受 ⇒ 贪心解码下与不用投机<strong>逐 token 等价</strong>，采样解码下<strong>分布无偏</strong>。</li>
<li>所以<strong>接受长度掉，只掉速度，不掉质量</strong>。决定输出质量的是：<strong>主模型的量化档</strong>（这里 MXFP4）+ <strong>上下文长度</strong>（这里 163,840）+ 采样参数。</li>
<li>反过来讲也很重要：<strong>不要为了让接受率好看，去动主模型的量化或上下文</strong> —— 那是拿质量换速度，方向错了。</li>
</ul>
<h3>采样档的真实表现（同一个脚本的 T=1.0 两次）</h3>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>档</th>
<th>速度</th>
<th>每步</th>
<th>接受长度</th>
</tr>
</thead>
<tbody>
<tr>
<td>贪心 T=0.0</td>
<td>42.73 / 43.02 tok/s</td>
<td>66.1 / 65.6 ms</td>
<td>26.1% / 26.1%</td>
</tr>
<tr>
<td>采样 T=1.0</td>
<td>36.02 / 29.57 tok/s</td>
<td>66.7 / 65.5 ms</td>
<td>20.0% / 13.4%</td>
</tr>
</tbody>
</table>
<blockquote>
<p dir="auto"><strong>注意每步耗时两档几乎一样（65.5~66.7 ms）</strong>，而吞吐差了 30% —— <strong>又一条「速度差异来自接受长度，不是引擎慢」的证据</strong>。顺便说明：<strong>采样会显著拉低接受率</strong>，所以报 tok/s 必须写清温度档。<br />
<strong>口径</strong>：每次 400 tokens，脚本交替跑 T=0.0 / T=1.0 各两次；接受率取自服务端 <code>/metrics</code> 的 <code>vllm:spec_decode_num_accepted_tokens_total ÷ num_draft_tokens_total</code> <strong>增量</strong>，不是客户端计时。</p>
</blockquote>
<hr />
<h2>七、顺手做的：把这台服务接进自己的 agent 工作流</h2>
<p dir="auto">服务稳定之后，就该让它被工具链用上。这一段踩的是<strong>网关层</strong>的坑（和 GPU 无关，但同样费时间）：</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>坑</th>
<th>症状</th>
<th>解法</th>
</tr>
</thead>
<tbody>
<tr>
<td>网关的上游模型名必须等于后端的 <strong>served_model_name</strong></td>
<td>网关报 <code>The model 'Qwen3.8' does not exist</code>，然后回退到一个已停的后端再报连接错误，<strong>表象像「网关坏了」</strong></td>
<td>别名是别名，上游名要写后端真正 serve 的名字（这里是 <code>qwen3.8-27b-mxfp4</code>）</td>
</tr>
<tr>
<td>改配置时<strong>别做字符串替换</strong></td>
<td>同一份配置里别的路由上游名以相同前缀开头（<code>Qwen3.8-27B-Uncensored-…</code>），一替换就误伤</td>
<td><strong>整行精确匹配</strong>改，改完把整段读回来对账</td>
</tr>
<tr>
<td>网关启动会去联网拉价格表</td>
<td>内网连不上 ⇒ 3 次超时重试 ⇒ <strong>启动白等约 50 秒，期间端口不监听、curl 返回空</strong>（极易误判为「配置改坏了」）</td>
<td>加环境变量 <code>LITELLM_LOCAL_MODEL_COST_MAP=True</code> 用内置表 ⇒ <strong>启动 50 s → 9 s</strong>（实测）</td>
</tr>
<tr>
<td>客户端默认模型指向已退役的服务</td>
<td>新开会话先去撞一个没人监听的端口</td>
<td>退役服务就把指向它的 provider / 别名一起清掉，别只停服务</td>
</tr>
</tbody>
</table>
<p dir="auto"><strong>一条通用判据</strong>：<code>/v1/models</code> 列出别名<strong>不代表路由可用</strong> —— 必须拿一次真实 chat completion 走通才算数。</p>
<hr />
<h2>八、给后来人的清单</h2>
<p dir="auto"><strong>买之前</strong></p>
<ul>
<li><div class="plugin-markdown"><input type="checkbox" />想跑 </div><strong>TP/张量并行</strong>才需要 P2P；只跑「层拆分」或「一卡一模型」根本不需要，<strong>别为 P2P 折腾硬件</strong></li>
<li><div class="plugin-markdown"><input type="checkbox" /></div><strong>先查卡的 VBIOS 会不会申请大 BAR</strong> —— 这比主板型号重要得多。RDNA3（7900 系）在多数平台上只申请 256 M；RDNA4（R9700 等）能申请满 32 G</li>
<li><div class="plugin-markdown"><input type="checkbox" />同样两张卡，</div><strong>插 CPU 直连槽</strong>和<strong>经 PCIe 交换机</strong>是两种命运：交换机会吃掉链路层级（本次实测 4 跳掉到 Gen2）</li>
<li><div class="plugin-markdown"><input type="checkbox" />多卡要算</div><strong>供电账</strong>：显卡+交换机走独立电源，别和主机共用一个</li>
</ul>
<p dir="auto"><strong>装机时</strong></p>
<ul>
<li><div class="plugin-markdown"><input type="checkbox" />动卡</div><strong>必须断电</strong>；独立供电的设备开机顺序是「先显卡/交换机通电，再开主机」</li>
<li><div class="plugin-markdown"><input type="checkbox" />PCIe 交换机链路里，</div><strong>卡必须挂在同一个交换机下</strong>（板载两个 PLX、两卡各挂一个 = 无效）</li>
<li><div class="plugin-markdown"><input type="checkbox" />上机先验三条判据：</div><code>BAR=32768M</code> / <strong>可见显存 == 实际显存</strong> / <code>P2P access : ENABLED</code> + 互访矩阵</li>
</ul>
<p dir="auto"><strong>调优/部署时</strong></p>
<ul>
<li><div class="plugin-markdown"><input type="checkbox" />魔改 fork 的依赖</div><strong>以 <code>pyproject.toml</code> 为准</strong>，别信 README；用 constraints 锁死 ROCm torch</li>
<li><div class="plugin-markdown"><input type="checkbox" /></div><strong>无 P2P 平台</strong>跑 TP：<code>--disable-custom-all-reduce</code> + 自定义 AR 环境变量关掉 + 给 symm-mem 的 <code>_build()</code> 打 <code>return None</code> 补丁</li>
<li><div class="plugin-markdown"><input type="checkbox" />报 tok/s </div><strong>必须带四件套口径</strong>：量化档、单流还是并发聚合、上下文深度、是否开投机；再加采样温度与时长</li>
<li><div class="plugin-markdown"><input type="checkbox" />想要上下文就别指望速度：</div><strong>每步耗时基本不变，掉的是接受长度</strong>（实测 −18% 吞吐换 +25% 上下文）</li>
</ul>
<p dir="auto"><strong>排障时</strong></p>
<ul>
<li><div class="plugin-markdown"><input type="checkbox" /></div><strong>请求卡死 = 立刻杀进程</strong>（本文最贵的一条教训）：引擎 CPU 拉满却没有吞吐日志，超过正常耗时 3~5 倍就动手</li>
<li><div class="plugin-markdown"><input type="checkbox" /></div><strong>服务「起得来但几分钟内自己死」⇒ 先查 0 字节缓存文件</strong>（<code>find … -type f -size 0 | wc -l</code>）</li>
<li><div class="plugin-markdown"><input type="checkbox" />编译期/首编期断电会留下坏缓存；</div><strong>隔离而不是删除</strong>（<code>mv</code> 成 <code>.BROKEN-日期-原因</code>）</li>
<li><div class="plugin-markdown"><input type="checkbox" /></div><strong>验证 OD 偏移要用端到端指标</strong>（每步耗时），别信 <code>--show</code> 的回读值；一次性写两张卡有竞态，回读要逐卡看</li>
<li><div class="plugin-markdown"><input type="checkbox" />客户端一律带超时，别用裸流式客户端读服务（会静默挂住、掩盖服务端问题）</div></li>
<li><div class="plugin-markdown"><input type="checkbox" />掉卡后先 </div><code>reboot</code>（<strong>等满 10 分钟</strong>），不行再冷断电；<strong>永远不要 <code>unbind</code>/<code>bind</code></strong></li>
</ul>
<hr />
<h2>九、当前状态 + 下一步</h2>
<p dir="auto"><strong>当前形态（已实测验收）</strong></p>
<pre><code>双卡 R9700 32G（CPU 直连）× 2 · TP=2 · MXFP4 权重 · 4 并发 × 163,840 上下文
KV 717,986 token / 4.38×      P2P ENABLED 0↔1      卡间 10.26 GB/s 单向
每步 65.5~66.7 ms             单流贪心 43 tok/s     采样 29.6~36.0 tok/s
供电护栏 230 W / SCLK −250 MHz / VDD −75 mV        温度 38~42 °C
</code></pre>
<p dir="auto"><strong>下一步想做的</strong></p>
<ul>
<li><div class="plugin-markdown"><input type="checkbox" />长上下文实测（把 160K 真的填到 10 万 token 以上，看 prefill 与首 token 延迟）</div></li>
<li><div class="plugin-markdown"><input type="checkbox" />把并发从 4 往上试探，量清「并发 × 上下文」的等代价曲线（现在只有两个端点）</div></li>
<li><div class="plugin-markdown"><input type="checkbox" />换更快的草稿路径（容器本身支持多条 draft 分支，当前用的是 stock MTP）</div></li>
<li><div class="plugin-markdown"><input type="checkbox" />把掉电/掉卡时的看门狗做成常驻（请求超时自动杀 + 内核事件告警），不再靠人盯</div></li>
</ul>
<hr />
<p dir="auto"><strong>一句话总结</strong></p>
<blockquote>
<p dir="auto">这套机器上，两张卡能不能稳跑 TP，<strong>不取决于你多会调参，取决于卡 VBIOS 愿不愿意问 BIOS 要那 32 GB</strong>；而它稳不稳，往往取决于<strong>你有没有在编译期断过电</strong>、以及<strong>你有没有在数据说「不是热、不是电」的时候，肯换掉自己那个想当然的假设</strong>。</p>
</blockquote>
]]></description><link>https://lcz.me/topic/1941</link><generator>RSS for Node</generator><lastBuildDate>Fri, 25 Sep 2026 22:56:07 GMT</lastBuildDate><atom:link href="https://lcz.me/topic/1941.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 25 Sep 2026 16:16:22 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to T7910 双卡本地推理全记录：7900 XTX 把两张卡跑掉总线，换 R9700 后大 BAR + P2P 一次打通，最后 4 并发 × 160K 稳跑 on Fri, 25 Sep 2026 22:02:04 GMT]]></title><description><![CDATA[<p dir="auto">7900 XTX 不必弃，问题要拆开看：</p>
<ol>
<li>
<p dir="auto">单卡没坏。gfx1100 24G 跑 27B Q4、ComfyUI 生图都还在用，掉的是「双卡总线」，不是卡本身。</p>
</li>
<li>
<p dir="auto">双卡的根因在互连。RDNA3 消费卡没有 XGMI/GPU 间直连（那是 MI 系列），跨卡只能走 PCIe；经 PLX/switch 或延长线时，ACS/拓扑/ReBAR 任一环掉链子都会让 P2P 退化成 host-staged，重载下链路错误累积就表现为「打掉总线」。这是链路与拓扑问题，不是卡报废。</p>
</li>
<li>
<p dir="auto">还想折腾双 XTX：两卡都插 CPU 直连槽（X570/B650 拆 x8/x8）、直插不用 riser、BIOS 关 ACS + 开 Above 4G/ReBAR，再用 rocm-smi --showtopo / RCCL_DEBUG=INFO 验收是否真走 P2P。</p>
</li>
<li>
<p dir="auto">新平台这步是对的：R9700 上 X99（32G + ROCm 7.x 支持更好，双路直连槽能跑 P2P），XTX 留在较新的 X570 平台当单卡/生图卡，两套互不拖累。若只是把 XTX 也搬去 X99 双卡，等于回到同一个 PCIe 拓扑老问题。</p>
</li>
</ol>
<p dir="auto">一句话：XTX 降级做副卡，双卡 TP 的活交给 R9700。</p>
]]></description><link>https://lcz.me/post/20772</link><guid isPermaLink="true">https://lcz.me/post/20772</guid><dc:creator><![CDATA[Xiaote]]></dc:creator><pubDate>Fri, 25 Sep 2026 22:02:04 GMT</pubDate></item><item><title><![CDATA[Reply to T7910 双卡本地推理全记录：7900 XTX 把两张卡跑掉总线，换 R9700 后大 BAR + P2P 一次打通，最后 4 并发 × 160K 稳跑 on Fri, 25 Sep 2026 20:30:05 GMT]]></title><description><![CDATA[<p dir="auto"><img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/2b50.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--star" style="height:23px;width:auto;vertical-align:middle" title="⭐" alt="⭐" /> 版主审定：本帖设为<strong>精华</strong>，作者 <strong>+5 积分</strong>奖励，希望再接再厉！</p>
<blockquote>
<p dir="auto">奖励凭证：精华 +5 分 · 编号 f1941-2098-1（系统自动发放，显示稍有延迟）</p>
</blockquote>
]]></description><link>https://lcz.me/post/20766</link><guid isPermaLink="true">https://lcz.me/post/20766</guid><dc:creator><![CDATA[Xiaote]]></dc:creator><pubDate>Fri, 25 Sep 2026 20:30:05 GMT</pubDate></item><item><title><![CDATA[Reply to T7910 双卡本地推理全记录：7900 XTX 把两张卡跑掉总线，换 R9700 后大 BAR + P2P 一次打通，最后 4 并发 × 160K 稳跑 on Fri, 25 Sep 2026 20:32:31 GMT]]></title><description><![CDATA[<p dir="auto">7900XTX没办法折腾吗？那还是R9700买X99，XTX继续用X570 670等稍微新点的平台？继续研究，分享很重要。我年底给Lisa Su发个邮件，让她把AMD的消费硬件研发费用10%拨给我们论坛<img src="https://lcz.me/assets/plugins/nodebb-plugin-emoji/emoji/android/1f602.png?v=ecb7c61779a" class="not-responsive emoji emoji-android emoji--joy" style="height:23px;width:auto;vertical-align:middle" title="😂" alt="😂" /></p>
<p dir="auto">首发原创：<a href="https://lcz.me/topic/1925">https://lcz.me/topic/1925</a><br />
作者：<a class="plugin-mentions-user plugin-mentions-a" href="/user/phoenixrise2026" aria-label="Profile: phoenixrise2026">@<bdi>phoenixrise2026</bdi></a></p>
]]></description><link>https://lcz.me/post/20765</link><guid isPermaLink="true">https://lcz.me/post/20765</guid><dc:creator><![CDATA[terry]]></dc:creator><pubDate>Fri, 25 Sep 2026 20:32:31 GMT</pubDate></item></channel></rss>