<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>nanami</title>
  <subtitle>nanami 的个人博客</subtitle>
  <link href="https://nanami233.com/" rel="alternate" type="text/html"/>
  <link href="https://nanami233.com/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://nanami233.com/</id>
  <updated>2026-10-27T00:00:00.000Z</updated>
  <entry>
    <title>RTX 4090 Tensor Core + PCIe P2P 双解锁：两台四卡服务器的部署与实测</title>
    <link href="https://nanami233.com/posts/rtx4090-tensor-unlock-pcie-p2p-warewulf/" rel="alternate" type="text/html"/>
    <id>https://nanami233.com/posts/rtx4090-tensor-unlock-pcie-p2p-warewulf/</id>
    <published>2026-10-27T00:00:00.000Z</published>
    <updated>2026-10-27T00:00:00.000Z</updated>
    <summary>从 Warewulf 无状态启动、Ada 固件前置解锁到 NVIDIA Open P2P、32GiB BAR1 和 NCCL 实测：一次可以复盘的 RTX 4090 GPU 节点改造。</summary>
    <content type="html"><![CDATA[<blockquote>
<p><strong>说明与脱敏</strong>：本文基于真实完成的双节点实验，主机名统一替换为 <code>compute-a</code> / <code>compute-b</code>，不包含真实域名、IP、MAC、个人账户、管理接口与设备 PCI BDF。所有管理和部署路径均为示例，请按自身环境替换。固件解锁与 NVIDIA 驱动补丁均非官方支持功能，可能引发设备不可用、数据错误、安全隔离问题或硬件风险；不要在缺乏带外恢复与回滚能力的生产环境直接执行。</p>
</blockquote>
<h2>先看结果</h2>
<p>我们在<strong>两台各有 4 张 RTX 4090 24GB</strong> 的 GPU 服务器上，结合 <a href="https://github.com/JustAResearcher/gpu-unlock-ada">gpu-unlock-ada</a> 与 <a href="https://github.com/Duanyll/open-gpu-kernel-modules">Duanyll 的 P2P 补丁</a> 完成了两类改造：</p>
<ul>
<li>Tensor Core 初始化：每台的 4 张卡均出现 <code>gpu_unlock: unlocked</code>；在特定 MMA 模式中，FP16/FP32 从 <strong>161.8</strong> 增至 <strong>323.6 TF/s</strong>，BF16/FP32 从 <strong>161.7</strong> 增至 <strong>323.4 TF/s</strong>，接近 <strong>2 倍</strong>。</li>
<li>BAR1：每卡由 <strong>256 MiB</strong> 扩至 <strong>32768 MiB（32 GiB）</strong>；这不是增加显存容量。</li>
<li>CUDA P2P：两台服务器共 <strong>24 条有向卡对</strong>全部数据校验通过，<code>errors=0</code>，单方向 64MiB 拷贝吞吐约 <strong>16–21 GiB/s</strong>。</li>
<li>NCCL：单机四卡 AllReduce 全部校验 <code>#wrong=0</code>。32–256MiB 下，<strong>默认 AUTO 拓扑策略约 18.5 GB/s</strong>，快于手动指定 PHB 与 SYS。</li>
<li>测试边界：这不是跨服务器八卡 NCCL 测试，也没有完成长期高负载稳定性认证。</li>
</ul>
<p><strong>最容易误解的地方</strong>：特定 MMA 指令 2 倍，不等于 GEMM 或 AI 训练整体 2 倍；<code>topo -p2p</code> 显示 OK，也不等于 NCCL 一定选 P2P，更不代表全局应用必然加速。</p>
<p><img src="/images/posts/rtx4090-unlock/cover.svg" alt="双节点 GPU、Tensor 解锁与 PCIe P2P 组合架构" /></p>
<p>与“复制命令然后宣布成功”不同，这次实验围绕三个实际问题展开：<strong>消费级 Ada 显卡究竟限制了什么、PCIe P2P 卡在哪里、为什么有些 NCCL 测试打开 P2P 后反而更慢？</strong> 我们逐一以固件初始化日志、PCIe 资源分配、CUDA 数据校验与 NCCL 重复实验回答。</p>
<p>:::note[如何阅读]
本文的配置和命令是经过脱敏的实验复盘，不是任何主板或驱动版本的通用“一键脚本”。先看机制与风险，再结合自己的 PCIe 拓扑、GSP/驱动版本和启动链修改。
:::</p>
<h2>1. 实验环境与双解锁机制</h2>
<table>
<thead>
<tr>
<th>项目</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>节点</td>
<td>2 台，每台 4 × RTX 4090 24GB</td>
</tr>
<tr>
<td>CPU 拓扑</td>
<td>双路 Xeon；GPU0/1、GPU2/3 位于不同 NUMA 域</td>
</tr>
<tr>
<td>计算节点</td>
<td>Fedora 44，内核 <code>6.19.10-300.fc44.x86_64</code></td>
</tr>
<tr>
<td>管理/启动</td>
<td>Warewulf 4.7，dracut + wwinit，Slurm</td>
</tr>
<tr>
<td>旧驱动</td>
<td>NVIDIA Open 610.57.04</td>
</tr>
<tr>
<td>新驱动</td>
<td>NVIDIA Open 615.71.09 + P2P Patch</td>
</tr>
<tr>
<td>固件解锁</td>
<td>gpu-unlock 1.1.1</td>
</tr>
<tr>
<td>CUDA Toolkit</td>
<td>13.3；驱动升级后 CUDA UMD 显示 13.4</td>
</tr>
<tr>
<td>NCCL</td>
<td>2.31.2；nccl-tests 2.21.1</td>
</tr>
</tbody>
</table>
<p>两个计算节点没有互联网。所有源码下载、RPM 准备、镜像处理均在联网管理端完成，计算节点只运行本地测试，再把日志拉回持久存储。</p>
<h3>1.1 Tensor Core 是初始化阶段的限制</h3>
<p><a href="https://github.com/JustAResearcher/gpu-unlock-ada">gpu-unlock-ada</a> 并不是传统意义上的超频。它通过预先生成的 AD102 固件资产和 <code>gpu_unlock.ko</code>，在 NVIDIA 驱动接管 GPU <strong>之前</strong>完成特定速率配置。正确的逻辑顺序是：</p>
<pre><code>冷启动 → initramfs 内 gpu_unlock + AD102 资产
      → NVIDIA Open Kernel Modules → CUDA / cuBLAS / NCCL
</code></pre>
<p>普通系统运行后再加载模块，不等价于在 GPU 固件初始化前介入。固件资产的取得和再分发必须遵守上游项目与原始许可证。</p>
<h3>1.2 P2P 是 GPU 显存映射与驱动能力问题</h3>
<p>RTX 4090 没有 NVLink。原始驱动即使枚举了四张卡，<code>nvidia-smi topo -p2p r/w</code> 仍可能全部显示 <code>GNS</code>。P2P 驱动补丁修改 NVIDIA Open 模块的 GPU peer mapping 支持；参考 <a href="https://duanyll.com/2026/7/13/4090-48G-P2P/">作者关于 4090 48GB、动态 BAR1 的技术文章</a>。</p>
<p><strong>BAR1 是 PCIe MMIO 可映射窗口，不是额外显存</strong>。GPU 自身报告 ReBAR 最高支持 32GB，并不代表 BIOS 当时实际给它分配了 32GB。本次初始 <code>current size</code> 只有 256MB。</p>
<h3>1.3 从 CPU 到 GPU：为什么 NUMA 位置决定通信上限？</h3>
<p>消费级 GPU 之间的 P2P 并不是虚拟的“GPU 私有高速总线”。它仍需走 GPU PCIe Endpoint → Root Port/PCIe Bridge → 对端 GPU 的物理路径。如果两卡落在不同 CPU Socket 的 Root Complex 后面，数据还会涉及处理器之间的互连；启用 P2P 能减少主机内存中转，但不能消除硬件跳数。</p>
<p><img src="/images/posts/rtx4090-unlock/topology.svg" alt="双路 NUMA 下四张 GPU 的拓扑示意" /></p>
<p>在我们的机器上，GPU0/1 和 GPU2/3 是两个相对接近的组合；GPU0→GPU2 属于跨 NUMA 通路。这一硬件结构解释了后面为什么同 NUMA 单向复制达到约 21 GiB/s，而部分跨 NUMA 方向约为 16–17 GiB/s。这里的图是<strong>拓扑示意而非真实机器的 PCI BDF/布线图</strong>。</p>
<h3>1.4 为什么必须由两个独立机制组合？</h3>
<p>Tensor Unlock 发生在 NVIDIA 驱动加载<strong>之前</strong>，目的是更改特定运算速率配置；P2P 补丁则是 NVIDIA Open Kernel Modules 层的 peer mapping 能力。二者在不同层面工作，既不能互相代替，也不意味着理论加速比可以相乘。</p>
<p>这也是我们先用一张卡验证 Tensor，再在克隆镜像中升级 NVIDIA 615 和 P2P、最后检查 GPU 间 NCCL 的原因。按阶段验收能把“启动失败”“GPU 数值错误”“通信错误”定位到不同层。</p>
<h2>2. 通过 Warewulf 部署 Tensor Unlock</h2>
<p>最重要的工程决策是：<strong>永远复制专用镜像，不在整个集群共用的 GPU 镜像上试错</strong>。下列镜像和目录名称都是示例：</p>
<pre><code>wwctl image copy fedora44-gpu-base fedora44-gpu-tensor
mkdir -p /srv/rtx4090-lab/src
git clone https://github.com/JustAResearcher/gpu-unlock-ada.git /srv/rtx4090-lab/src/gpu-unlock-ada

cd /srv/rtx4090-lab/src/gpu-unlock-ada
git rev-parse HEAD
make inputs
make firmware
make test-full
make rpm
</code></pre>
<p>本次 <code>test-full</code> 执行 33 项测试，30 通过、3 项可选离线解码测试跳过。生成 <code>gpu-unlock-firmware</code>、<code>gpu-unlock-dkms</code> 两个 RPM，在 Fedora 44 独立镜像里安装。注意 RPM 是在另一发行版构建机上生成的 <code>noarch</code> 包，因此不能只看安装成功，要实查 <strong>Fedora 内核版本对应的 DKMS 状态</strong>：</p>
<pre><code>KVER=6.19.10-300.fc44.x86_64
dkms status
modinfo -k "$KVER" gpu_unlock
cat /usr/lib/dracut/dracut.conf.d/gpu-unlock.conf
</code></pre>
<p>本次成功后可以看到：</p>
<pre><code>gpu-unlock/1.1.1, 6.19.10-300.fc44.x86_64, x86_64: installed
force_drivers+=" gpu_unlock "
</code></pre>
<p><strong>容易踩的坑：Warewulf 的 PXE/wwinit 启动链不等于普通磁盘系统的 GRUB。</strong> 仅把模块放进镜像文件系统并不能确保它在 NVIDIA 驱动之前运行。目标 Fedora 镜像中生成 dracut initramfs，并明确加入 wwinit：</p>
<pre><code>KVER=6.19.10-300.fc44.x86_64
INITRD=/boot/initramfs-$KVER.img
dracut --force --no-hostonly --add wwinit "$INITRD" "$KVER"
lsinitrd -m "$INITRD" | grep wwinit
lsinitrd "$INITRD" | grep -E 'gpu_unlock|v67-ad102|gsp_boot_ad102'
</code></pre>
<p>随后退出镜像，使用 <code>wwctl image build fedora44-gpu-tensor</code>。实际节点需确认其 iPXE 模式就是 dracut 路径，启动参数确实传入解锁模块。内核模块和 AD102 固件应出现在<strong>真正的早期启动环境</strong>；NVIDIA 完整驱动可以后续加载，不能仅凭早期 initramfs 里没有 <code>nvidia.ko</code> 就判为失败。</p>
<p>第一次只针对一张 RTX 4090，在节点专用启动参数中配置 <code>gpu_unlock.targets=&lt;GPU_BDF&gt;</code>。BDF 必须从目标机器的 <code>lspci -Dnn</code> 查询，<strong>不可把另一台的 PCI 地址照搬过去</strong>。单卡日志确认 <code>gpu_unlock: unlocked</code> 后，再扩展为四张。Warewulf 的 <code>Kernel.Args</code> 是逗号列表：多 BDF 字符串要作为<strong>一个参数元素</strong>传入，并核对 <code>/proc/cmdline</code>，避免默认 profile 参数重复。</p>
<p>初次测试在设备重新上电后完成；内核出现过模块签名未被信任的 taint 日志，但并未阻止解锁。正式 Secure Boot 环境应另行处理模块签名信任链。</p>
<h2>3. 升级 NVIDIA 615.71.09 并编译 P2P 补丁</h2>
<h3>3.1 驱动版本必须匹配</h3>
<p>目标分支为 <code>615.71.09-p2p-48g</code>。原环境的 610.57.04 用户态驱动不能直接混装 615 的内核模块。第二个 Warewulf 镜像从<strong>已通过 Tensor 验证的镜像</strong>复制，保留可回滚的旧镜像。</p>
<p>Fedora 44 的 NVIDIA 仓库提供 615.71.09 的用户态组件与 Open DKMS 包。先用精确版本做 <code>dnf install --assumeno</code> 事务预演，再安装同版的 <code>nvidia-driver-common</code>、<code>nvidia-driver-cuda</code>、<code>nvidia-driver-cuda-libs</code>、<code>nvidia-kmod-common</code>、<code>kmod-nvidia-open-dkms</code>、<code>nvidia-modprobe</code>、<code>nvidia-persistenced</code>。</p>
<p>这里曾踩过一个关键依赖坑：预演时为了避免官方 DKMS 覆盖补丁，排除了 <code>kmod-nvidia-open-dkms</code>，却使 <code>nvidia-kmod-common</code> 所需的 <code>nvidia-kmod = 615.71.09</code> 依赖缺失。解决方法是<strong>先完整升级官方 615.71.09 包组，再单独构建补丁模块</strong>。</p>
<p>另一个细节：RPM 安装成功后 <code>dkms status</code> 显示 <code>nvidia/615.71.09: added</code>，但 <code>modinfo</code> 查询不到 NVIDIA 模块。<code>added</code> 只是已注册源码，并不是已构建并安装。</p>
<h3>3.2 在 Fedora 44 内核开发环境构建</h3>
<p><code>home</code> 类型的管理端即使有网，也不能使用管理端 Rocky 内核头文件替 Fedora 44 编译模块。源代码先从管理端只读 bind 到镜像，再复制到镜像本地构建：</p>
<pre><code>git clone https://github.com/Duanyll/open-gpu-kernel-modules.git
cd open-gpu-kernel-modules
git switch 615.71.09-p2p-48g
git rev-parse HEAD
cat version.mk

# 以下 make 在 Fedora 44 构建镜像内进行
KVER=6.19.10-300.fc44.x86_64
make -j16 modules SYSSRC="/usr/src/kernels/$KVER" SYSOUT="/usr/src/kernels/$KVER"
</code></pre>
<p>生成并核实版本的模块有：</p>
<pre><code>nvidia.ko
nvidia-modeset.ko
nvidia-uvm.ko
nvidia-drm.ko
nvidia-peermem.ko
</code></pre>
<p>五个模块的 <code>modinfo version</code> 都是 <code>615.71.09</code>，<code>vermagic</code> 均匹配 <code>6.19.10-300.fc44.x86_64</code>。编译中存在 <code>objtool</code> 的 <code>naked return</code> 警告及缺少 <code>vmlinux</code> 导致的 BTF 生成跳过，但链接成功；是否运行正确仍以后续 GPU 实测为准。</p>
<p>本次首次验证把这些补丁模块安装到独立镜像的 <code>/lib/modules/&lt;KVER&gt;/updates/dkms/</code>，执行 <code>depmod -a "$KVER"</code>，再用 <code>modinfo -k "$KVER" -F filename nvidia</code> 验证最终模块优先路径。重新生成包含 <code>wwinit</code> 与 <code>gpu_unlock</code> 的 dracut initramfs，随后 <code>wwctl image build</code>，最后只切换两台已排空的节点。</p>
<p><strong>不能忽视的运维技术债</strong>：P2P 模块最初是手工放入 <code>updates/dkms</code>，而官方 <code>nvidia/615.71.09</code> DKMS 源码注册项可能仍存在。将来执行 <code>dkms autoinstall</code> 或升级驱动会有覆盖补丁的风险。真正长期维护应封装成版本固定、可重建的补丁 DKMS/RPM，而不是永久依赖一次性手工覆盖。</p>
<h2>4. BIOS / BAR1 / IOMMU 配置</h2>
<p><img src="/images/posts/rtx4090-unlock/bar1.svg" alt="BAR1：映射窗口与物理显存的区别" /></p>
<h3>4.1 BAR1 扩容不是显存扩容</h3>
<p>CPU 可通过 PCIe MMIO 窗口访问 GPU 的部分设备/显存地址空间。BAR1 资源的“size”表示主机为这个 PCIe BAR 分配的<strong>地址窗口容量</strong>，而不是 GPU 搭载了多少显存。大 BAR1 对静态映射覆盖整卡显存非常关键，但它也占用平台的 PCIe MMIO 地址空间；四卡同时选择 32GiB BAR1，理论上需要约 128GiB 的窗口地址资源（还未计入其他 BAR）。</p>
<p>动态 BAR1 映射并非简单“开了 ReBAR 就变成动态”：它还依赖相应的驱动映射策略。我们这里的卡是<strong>标准 24GB</strong>，而原始研究重点包括 <strong>48GB 改装卡</strong>。24GB 容量小于已分配的 32GiB 窗口，因此不能仅凭本次结果证明 48GB 场景下的动态窗口回收算法已被充分验证。本文只声称本次 24GB 环境的 peer copy / NCCL 结果。</p>
<h3>4.2 从能力位到实际映射有三层检查</h3>
<ol>
<li>§lspci -vv§ 的 Resizable BAR <strong>supported sizes</strong>：GPU 宣称支持哪些窗口。</li>
<li>§lspci -vv§ 的 <strong>current size</strong> 与 §nvidia-smi -q§ 的 BAR1 Total：平台实际上分配了多少。</li>
<li>§nvidia-smi topo -p2p§、CUDA Peer Access 与数据一致性：驱动和上层运行时是否真正可用。</li>
</ol>
<p>即便第三层返回 OK，也仍需要验证集合通信的数值和性能。</p>
<p>两台机器的 GPU 都报告 <code>Physical Resizable BAR</code> 支持 BAR1 最大 <strong>32GB</strong>；但刚开始 <code>lspci -vv</code> 的 <strong>current size</strong> 只有 <strong>256MB</strong>。</p>
<p>维护窗口使用带外控制台检查主板 BIOS/UEFI：</p>
<table>
<thead>
<tr>
<th>设置</th>
<th>本次方向</th>
<th>为什么</th>
</tr>
</thead>
<tbody>
<tr>
<td>Above 4G Decoding</td>
<td>启用</td>
<td>为大 PCIe MMIO 预留地址空间</td>
</tr>
<tr>
<td>Resizable BAR</td>
<td>有此选项则启用</td>
<td>允许扩大 BAR1</td>
</tr>
<tr>
<td>High MMIO 相关配置</td>
<td>按主板文档确保充足</td>
<td>四卡 32GiB 共约 128GiB 的窗口地址需求</td>
</tr>
<tr>
<td>CSM / UEFI</td>
<td>与既有 UEFI 启动保持一致</td>
<td>防止变更造成不能启动</td>
</tr>
<tr>
<td>PCIe 链路</td>
<td>Auto / 合理的 Gen4 配置</td>
<td>空闲的 2.5GT/s 不等于负载最大速率</td>
</tr>
<tr>
<td>IOMMU</td>
<td>在可信环境中评估 passthrough</td>
<td>对 DMA 隔离存在安全权衡</td>
</tr>
</tbody>
</table>
<p>我们将节点内核 IOMMU 默认域切为 passthrough，实际日志为：</p>
<pre><code>iommu: Default domain type: Passthrough (set via kernel command line)
</code></pre>
<p><strong>安全提醒</strong>：<code>iommu=pt</code> 改变 DMA 隔离语义，不应不加评估地应用在多租户/不可信 PCIe 设备环境。也不要为“让拓扑好看”盲目设置 <code>pcie_acs_override</code>，它不会改变物理 PCIe 路径。</p>
<p>冷启动后，每张卡的 <code>nvidia-smi -q</code> 均显示 <code>BAR1 Total: 32768 MiB</code>，<code>nvidia-smi topo -p2p r</code>、<code>w</code> 中 12 个非对角有向卡对全部为 <code>OK</code>。这是能力枚举成功，尚需 CUDA 与 NCCL 数据验证。</p>
<h2>5. Tensor Core 实测：2 倍的是哪一层？</h2>
<p>首次单卡验证时以一张解锁卡对照一张未解锁卡，同条件运行项目的 <code>mma_bench</code> 与 <code>gemm_bench</code>，数值测试均为 <code>failures=0</code>、<code>max_abs=0</code>。跨物理卡的对比仍可能受到功耗、时钟和调度差异影响，因此报告实测而非保证。</p>
<h3>MMA 指令吞吐（best TF/s）</h3>
<p><img src="/images/posts/rtx4090-unlock/mma.svg" alt="Tensor Core MMA：原始卡与解锁卡对照" /></p>
<p>指令层基准特意保留未变化的 INT8、FP16/FP16 和普通 FP32 FFMA 作为<strong>负对照</strong>。如果所有模式都同幅度上升，就需要怀疑是频率、功耗或测试方法而不只是某个 Tensor 模式的限制被解除。</p>
<table>
<thead>
<tr>
<th>模式</th>
<th>原始/对照</th>
<th>解锁</th>
<th>加速</th>
</tr>
</thead>
<tbody>
<tr>
<td>FP16/FP32</td>
<td>161.8</td>
<td><strong>323.6</strong></td>
<td><strong>2.00×</strong></td>
</tr>
<tr>
<td>BF16/FP32</td>
<td>161.7</td>
<td><strong>323.4</strong></td>
<td><strong>2.00×</strong></td>
</tr>
<tr>
<td>FP8 E4M3</td>
<td>323.3</td>
<td><strong>644.5</strong></td>
<td><strong>1.99×</strong></td>
</tr>
<tr>
<td>FP16/FP16</td>
<td>323.7</td>
<td>323.7</td>
<td>1.00×</td>
</tr>
<tr>
<td>INT8/INT32</td>
<td>646.9</td>
<td>646.9</td>
<td>1.00×</td>
</tr>
<tr>
<td>FP32 FFMA</td>
<td>77.3</td>
<td>77.4</td>
<td>约 1.00×</td>
</tr>
</tbody>
</table>
<p><strong>不是所有 Tensor 模式、所有精度都翻倍</strong>。</p>
<h3>cuBLAS GEMM 中位吞吐（TF/s）</h3>
<p><img src="/images/posts/rtx4090-unlock/gemm.svg" alt="cuBLAS GEMM 中位吞吐对比" /></p>
<p>GEMM 的实际吞吐包含 Kernel 实现、访存、矩阵维度与调度成本。特别是 BF16/FP32 4096² 只改善约 0.6%，提示这类任务可能不是被解锁的指令速率控制。</p>
<table>
<thead>
<tr>
<th>算例</th>
<th>原始/对照</th>
<th>解锁</th>
<th>改善</th>
</tr>
</thead>
<tbody>
<tr>
<td>FP16/FP32，1024²</td>
<td>110.38</td>
<td>126.72</td>
<td>+14.8%</td>
</tr>
<tr>
<td>FP16/FP32，4096²</td>
<td>163.09</td>
<td>199.54</td>
<td><strong>+22.3%</strong></td>
</tr>
<tr>
<td>BF16/FP32，4096²</td>
<td>152.31</td>
<td>153.22</td>
<td>+0.6%</td>
</tr>
<tr>
<td>FP16/FP32，8192²</td>
<td>171.07</td>
<td>189.02</td>
<td>+10.5%</td>
</tr>
<tr>
<td>BF16/FP32，8192²</td>
<td>167.42</td>
<td>187.42</td>
<td>+11.9%</td>
</tr>
</tbody>
</table>
<p>这里已经能看到：指令性能即便 2×，应用 GEMM 的吞吐提升也会受算子实现、访存和调度影响。</p>
<h2>6. CUDA Peer Copy 实测：24 条方向全部 PASS</h2>
<p>每个方向使用 64MiB 数据，重复 20 次；4 张 GPU 每节点共有 12 条有向卡对，两台共 <strong>24 条</strong>。所有卡对的 <code>peer_access=1</code>、<code>correct=1</code>，结果均为：</p>
<pre><code>RESULT: PASS; errors=0
</code></pre>
<p>挑选代表性路径（GiB/s）：</p>
<table>
<thead>
<tr>
<th>路径</th>
<th>compute-a</th>
<th>compute-b</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPU0 → GPU1，同 NUMA</td>
<td>21.321</td>
<td>21.334</td>
</tr>
<tr>
<td>GPU2 → GPU3，同 NUMA</td>
<td>21.187</td>
<td>21.194</td>
</tr>
<tr>
<td>GPU0 → GPU2，跨 NUMA</td>
<td>16.040</td>
<td>16.027</td>
</tr>
<tr>
<td>GPU2 → GPU0，跨 NUMA</td>
<td>17.311</td>
<td>17.298</td>
</tr>
</tbody>
</table>
<p>跨 NUMA 通常比同 NUMA 慢，这是物理主机桥/处理器互连拓扑的结果。此表是<strong>单向拷贝带宽</strong>，不是 NCCL 的 <code>busbw</code>。</p>
<h2>7. NCCL：为什么四卡结果没有“暴涨”？</h2>
<p>从 <a href="https://github.com/NVIDIA/nccl">NVIDIA NCCL</a> 源码构建 <strong>2.31.2</strong>，测试采用 <a href="https://github.com/NVIDIA/nccl-tests">nccl-tests</a> <strong>2.21.1</strong>。计算节点未安装 NCCL RPM，官方 CUDA 仓库中也未找到该 Fedora 版本的 <code>libnccl-devel</code>，故由联网端取源码并在 Fedora 构建镜像编译。</p>
<p>当时 <code>nvcc 13.3</code> 报：</p>
<pre><code>#error -- unsupported GNU version!
gcc versions later than 15 are not supported!
</code></pre>
<p>Fedora 44 默认 GCC 16，因此改为安装兼容 <code>gcc15</code>/<code>gcc15-c++</code>，明确传入 <code>CC=/usr/bin/gcc-15</code>、<code>CXX=/usr/bin/g++-15</code>，没有直接绕过 <code>nvcc</code> 的编译器版本检查。期间还曾因 <code>cd</code> 失败而误在 NCCL 源码目录再次 <code>make</code>，耗时很长；之后通过核对工作目录，只构建测试程序。</p>
<h3>7.1 单机四卡，开启与关闭 P2P</h3>
<p>首轮 8–512MiB AllReduce，平均 <code>busbw</code>（GB/s）：</p>
<table>
<thead>
<tr>
<th>节点</th>
<th>P2P Enabled</th>
<th>P2P Disabled</th>
</tr>
</thead>
<tbody>
<tr>
<td>compute-a</td>
<td>18.4579</td>
<td>18.4074</td>
</tr>
<tr>
<td>compute-b</td>
<td>18.0705</td>
<td>18.3846</td>
</tr>
</tbody>
</table>
<p><strong>这组数字并未证明四卡 AllReduce 因 P2P 大幅加速。</strong> 某些配置甚至更慢，属于需要进一步分解 NCCL 运行策略的问题。</p>
<p>但开启详细初始化日志并设置 <code>NCCL_P2P_LEVEL=SYS</code> 后，两台的 NCCL 明确报告：</p>
<pre><code>Check P2P Type isAllDirectP2p 1 directMode 1 isAllCudaP2p 1
Channel ... via P2P/direct pointer
</code></pre>
<p>证明该诊断实验里 NCCL 确实使用了 CUDA 直接 P2P 路径。<code>SYS</code> 是允许访问的最远拓扑级别，不是“一定选最快路由”的开关。</p>
<h3>7.2 两卡的 P2P 收益很明显</h3>
<p>在其中一台服务器上，32–256MiB 双卡 AllReduce 平均 <code>busbw</code>（GB/s）：</p>
<table>
<thead>
<tr>
<th>卡对</th>
<th>P2P 开</th>
<th>P2P 关</th>
<th>提升</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPU0–GPU1</td>
<td>19.0955</td>
<td>15.8076</td>
<td><strong>+20.8%</strong></td>
</tr>
<tr>
<td>GPU2–GPU3</td>
<td>19.1395</td>
<td>15.7801</td>
<td><strong>+21.3%</strong></td>
</tr>
<tr>
<td>GPU0–GPU2（跨 NUMA）</td>
<td>17.2592</td>
<td>13.7892</td>
<td><strong>+25.2%</strong></td>
</tr>
</tbody>
</table>
<p>全部 <code>#wrong=0</code>。跨 NUMA 禁用 P2P 的数据存在一次明显低值，<code>+25.2%</code> 可能有异常值放大效应，不能无条件外推。</p>
<h3>7.3 三轮复测：AUTO 比强制 PHB / SYS 更快</h3>
<p><img src="/images/posts/rtx4090-unlock/nccl.svg" alt="四卡 NCCL AllReduce：AUTO、PHB、SYS 的重复实验" /></p>
<p>为何不把双卡的 21% 改善直接推广到四卡？因为 AllReduce 不是一次独立 Peer Copy，而是涉及同步、分块、通信环/树与不同拓扑边的集合操作；最慢链路、通道选择和 CPU Socket 间路径会影响总吞吐。强制扩大 P2P 允许距离，可能令 NCCL 不再采用其默认认为更好的组合。</p>
<p>因此我们固定消息大小与迭代数量，对 AUTO、PHB、SYS 各重复 3 次，并以所有轮次的平均 §busbw§ 讨论性能，而不是挑最快的一条数据。</p>
<p>32–256MiB 单机四卡 AllReduce，每策略 3 次，平均 <code>busbw</code>（GB/s）：</p>
<table>
<thead>
<tr>
<th>NCCL 策略</th>
<th>compute-a</th>
<th>compute-b</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>AUTO（不设置 P2P_LEVEL）</strong></td>
<td><strong>18.4943</strong></td>
<td><strong>18.4803</strong></td>
</tr>
<tr>
<td>PHB</td>
<td>16.9091</td>
<td>16.9329</td>
</tr>
<tr>
<td>SYS</td>
<td>16.5636</td>
<td>16.5749</td>
</tr>
</tbody>
</table>
<p>AUTO 比 PHB 快约 <strong>9.4%</strong>，比 SYS 快约 <strong>11.6%</strong>。全部 18 次测试正常结束，<code>#wrong</code> 均为 0，没有越界数据错误。AUTO 由 NCCL 自己选择拓扑和通道，<strong>不要把调试时的 <code>NCCL_P2P_LEVEL=SYS</code> 永久写到集群全局环境</strong>。但 AUTO 的详细底层路径仍需要针对具体任务诊断，不能仅凭总线带宽反推所有环节的传输方式。</p>
<p>推荐维持默认：</p>
<pre><code>unset NCCL_P2P_LEVEL
unset NCCL_P2P_DISABLE
unset NCCL_ALGO
unset NCCL_PROTO
</code></pre>
<h3>7.4 对性能指标的解释</h3>
<p>NCCL 的 §algbw§ 是算法层有效带宽；§busbw§ 是依据通信原语和卡数折算的总线带宽，<strong>不能直接与单向 CUDA Peer Copy 的 GiB/s 作数值等价比较</strong>。另外本次 §GB/s§ 为 NCCL 的十进制单位，Peer Copy 的 §GiB/s§ 为二进制单位，单位差异也是比较时必须说明的。</p>
<p>实验证据分四级：</p>
<table>
<thead>
<tr>
<th>证据</th>
<th>能证明什么</th>
<th>不能证明什么</th>
</tr>
</thead>
<tbody>
<tr>
<td>§gpu_unlock: unlocked§</td>
<td>固件阶段报告解锁</td>
<td>所有应用都加速</td>
</tr>
<tr>
<td>§topo -p2p OK§</td>
<td>运行时宣称可访问</td>
<td>数据必定正确</td>
</tr>
<tr>
<td>24/24 Peer Copy PASS</td>
<td>指定大小/方向数据正确</td>
<td>NCCL 必选此路径</td>
</tr>
<tr>
<td>§via P2P/direct pointer§ + AllReduce</td>
<td>NCCL 此次使用直接指针、数值通过</td>
<td>全部模型训练都提升</td>
</tr>
</tbody>
</table>
<p>这四级证据也是本文最希望提供的一套评估方法。</p>
<h2>8. 这次最有用的踩坑清单</h2>
<ol>
<li><strong>Warewulf bind 的 <code>not a directory</code>：</strong> 目的路径可能在挂载失败时留下 0 字节普通文件。先 <code>stat</code> 确认、清理该特定空文件，再创建目录；不要盲目递归删除。</li>
<li><strong>交互 Shell 意外退出：</strong> 直接启用 <code>set -euo pipefail</code>，后续命令出错可能退出当前交互 Shell。长步骤使用单独 <code>bash</code> 脚本并保留日志。</li>
<li><strong><code>DKMS added</code> ≠ <code>installed</code>：</strong> 不要仅以 RPM 安装完成判断 NVIDIA 内核模块存在；看 <code>modinfo -k</code> 与 <code>vermagic</code>。</li>
<li><strong>dracut 的 <code>No /dev/log</code> 提示：</strong> 需结合真实退出码、<code>lsinitrd -m</code> 的 <code>wwinit</code>、固件和模块检查判断，不应只看日志中 <code>[E]</code> 就宣告失败。</li>
<li><strong>NFS root_squash：</strong> 无状态节点直接以 root 写 NFS 可能遇到 Permission denied；测试结果先写节点本地，再从管理端主动拉取归档。</li>
<li><strong>错误地在 NCCL 源码里运行 make：</strong> 由于上一条 <code>cd</code> 失败，后续 <code>make</code> 启动了完整 NCCL 构建。关键目录切换用 <code>cd ... || exit 1</code> 防止误操作。</li>
<li><strong>INFO 日志污染性能输出：</strong> NCCL <code>INFO</code> 可能与性能表格输出交错；性能跑分用 <code>WARN</code>，路径诊断单独保留 <code>INFO</code> 日志。</li>
</ol>
<h2>9. 运维结论和下一步</h2>
<h3>9.1 可回滚比“一次成功”重要</h3>
<p>维护期应先通过 Slurm drain 保护作业，保留原始 NVIDIA 610 镜像、仅 Tensor 解锁的已知可用镜像，以及 615+P2P 独立测试镜像。任何 boot 参数（尤其 IOMMU、GPU BDF）须在节点级设置并检查实际 §/proc/cmdline§；不要改共享 GPU profile 后让另一台不相关节点意外继承。</p>
<p>除冷启动恢复，还应把补丁源码提交哈希、目标内核 ABI、最终 §modinfo filename/version/vermagic§、NCCL 测试日志等收进可追踪的维护记录。</p>
<p>这次已实证完成：两个节点的 Tensor 解锁、32GiB BAR1、24/24 CUDA P2P 有向传输校验、NCCL 直接指针路径验证及短时 AllReduce 数值正确性。<strong>双卡 P2P 收益明显，四卡应保留 NCCL AUTO。</strong></p>
<p>尚未完成的是长时间满载老化测试、跨节点八卡 NCCL 以及<strong>把手工补丁模块纳入正式 DKMS/RPM</strong> 的持久化管理。特别是驱动/内核自动升级可能覆盖 <code>updates/dkms</code> 的补丁模块；应固定镜像版本、保留可冷启动回滚的原镜像，并在实际科研或训练负载中继续记录温度、功耗、Xid 和 PCIe AER。</p>
<p>这次最大的经验不是某个跑分数字，而是要把四件事分别验收：<strong>驱动初始化成功、CUDA 能直接通信、NCCL 实际选择直接通信、应用端最终得到性能收益</strong>。没有这四层分离，就很容易把“能用”误写成“加速已经完成”。</p>
<h2>参考</h2>
<ul>
<li><a href="https://github.com/JustAResearcher/gpu-unlock-ada">JustAResearcher/gpu-unlock-ada</a></li>
<li><a href="https://github.com/Duanyll/open-gpu-kernel-modules">Duanyll/open-gpu-kernel-modules</a></li>
<li><a href="https://duanyll.com/2026/7/13/4090-48G-P2P/">RTX 4090 48G P2P 与动态 BAR1 技术文章</a></li>
<li><a href="https://github.com/NVIDIA/nccl">NVIDIA NCCL</a></li>
<li><a href="https://github.com/NVIDIA/nccl-tests">NVIDIA nccl-tests</a></li>
<li><a href="https://warewulf.org/">Warewulf</a></li>
</ul>
]]></content>
    <author><name>nanami</name></author>
    <category term="HPC / GPU"/>
  </entry>
</feed>
