NNetpath
原理深入预计 35 分钟

PCIe:机内的那张网

PCIe 不是总线而是一张交换网。槽位插错、代际不对,400G 网卡只能跑出一半。

学完这节你能做到

  • 按代际与 lane 数算出一条 PCIe 链路的可用带宽,并判断它够不够喂满某张网卡
  • 用 lspci 与 nvidia-smi topo 读出实际协商结果与设备间的拓扑关系
  • 说出 GPUDirect 对 PCIe 拓扑与 ACS 的要求

建议先学跳过这几节会看不懂本节的部分推导

PCIe 不是总线,是一张交换网

名字里带"总线"(Bus)是历史包袱。PCI 时代确实是共享总线,PCIe 从第一代起就是点对点的交换网络—— 结构和一张以太网非常像:

              ┌──────────┐
     CPU ─────│   Root   │  Root Complex:CPU 侧的入口,相当于网关
              │ Complex  │
              └────┬─────┘
                   │  x16 链路(相当于一条网线)
              ┌────▼─────┐
              │ PCIe     │  Switch:相当于交换机
              │ Switch   │
              └──┬────┬──┘
                 │    │
              ┌──▼─┐┌─▼──┐
              │GPU ││NIC │  Endpoint:相当于终端设备
              └────┘└────┘

这个类比一旦建立,L0 学的那套思路就能直接套过来:有链路就有带宽上限,有交换机就有拓扑远近, 有协商就可能协商错。 这一节要回答的就是这三件事。

带宽账:每代每 lane 多少

一条 lane 是一对差分线(收发各一对),所以 PCIe 天生全双工

代际每 lane 速率编码每 lane 有效(单向)x8(单向)x16(单向)
Gen38 GT/s128b/130b≈ 0.985 GB/s≈ 7.9 GB/s≈ 15.75 GB/s
Gen416 GT/s128b/130b≈ 1.97 GB/s≈ 15.8 GB/s≈ 31.5 GB/s
Gen532 GT/s128b/130b≈ 3.94 GB/s≈ 31.5 GB/s≈ 63 GB/s
Gen664 GT/sPAM4 + FLIT≈ 8 GB/s≈ 64 GB/s≈ 128 GB/s

两个规律记住就够:每代翻倍、lane 数线性。所以 Gen5 x8 ≈ Gen4 x16 ≈ 31.5 GB/s—— 这个等价关系在排查"为什么插上去只有一半带宽"时会反复用到。

×单向还是双向:最常见的 2 倍误差

到处都能看到"Gen5 x16 = 128 GB/s"。这个数字是双向聚合(2 × 63), 而同一份资料里的 Gen4 数字往往写的是单向 32 GB/s——两个口径混在一起比,就凭空差了一倍。

这和 L0 里 Gbps 与 GB/s 的换算是同一类错误,而且更隐蔽。本站统一口径: PCIe 一律写单向,因为要和网卡的"每端口每方向"对齐;NVLink 官方只给双向聚合值, 下一节会单独标出来。

看到任何 PCIe 带宽数字,第一个问题永远是:这是单向还是双向?

关键换算:这张网卡插得下吗

这是本节最有实用价值的一算。网卡带宽换成 GB/s,再看要占多少 lane:

网卡单向带宽最低要求Gen4 x16 够吗
25 GbE3.1 GB/sGen3 x4富余
100 GbE12.5 GB/sGen3 x16 / Gen4 x8
200 Gb(HDR)25 GB/sGen4 x16 / Gen5 x8刚好
400 Gb(NDR)50 GB/sGen5 x16不够(31.5 < 50)
800 Gb(XDR)100 GB/sGen6 x16不够

一张 400G 网卡插在 Gen4 x16 槽位上,理论上限就只有 31.5 GB/s,约等于 250 Gb。 NCCL 测出来的 busbw 会稳定停在六成左右,而 ibstat 显示链路速率 400、 ib_send_bw 点对点也能跑很高(单向短测打不满 PCIe),排查时极容易被绕进去。

L2 闯关里那个「只有一半」,这里是它的另一个可能来源

之前 AllReduce 只有理论值一半 那一关, 根因是 NCCL_IB_HCA 少列了卡。但同样的症状还有一个来源就在这里: 网卡插在了代际或 lane 数不够的槽位上。

区别方法:NCCL_IB_HCA 的问题在日志里能看到卡数不对;PCIe 的问题要看 lspci 的协商结果。两个都要查,顺序上先看日志(快),再看槽位。

核对实际协商结果

设备能力(Capability)和当前状态(Status)是两回事,只看前者会被骗:

# 找到网卡的 BDF 地址
lspci | grep -i mellanox
# 31:00.0 Ethernet controller: Mellanox Technologies MT2910 Family [ConnectX-7]

# 对比能力与实际协商
lspci -vv -s 31:00.0 | grep -E 'LnkCap|LnkSta'
LnkCap: Port #0, Speed 32GT/s, Width x16, ...      ← 能力:Gen5 x16
LnkSta: Speed 32GT/s, Width x16                    ← 实际:Gen5 x16 ✓
LnkCap: Port #0, Speed 32GT/s, Width x16, ...      ← 能力:Gen5 x16
LnkSta: Speed 16GT/s (downgraded), Width x8 (downgraded)   ← 实际掉到 Gen4 x8 ✗

(downgraded) 这个标记是白送的诊断信息。降速的常见原因:

现象常见原因
Width 掉一半插在了电气 x8 的物理 x16 槽位;或 bifurcation 配置把 x16 拆了
Speed 掉一代BIOS 里锁了代际;转接卡/延长线信号质量不够;主板走线过长
时好时坏卡没插到底、金手指氧化、机箱震动
# 看整机拓扑树,确认设备挂在哪个 switch 下
lspci -tv

# 看槽位与 NUMA 归属
lspci -vv -s 31:00.0 | grep -E 'NUMA|Physical Slot'

拓扑:远近决定性能

nvidia-smi topo -m 输出的那些缩写就是"距离",从近到远:

标记含义代价
NV#走 NVLink,# 是链路数最快(下一节讲)
PIX同一个 PCIe Switch最优的 PCIe 路径
PXB跨多个 PCIe Switch略有增加
PHB经过 Host Bridge(Root Complex)要过 CPU 侧
NODE同一 NUMA 节点内跨 Host Bridge更远
SYS跨 NUMA / 跨 socket最差,要过 CPU 互联(UPI/xGMI)
nvidia-smi topo -m

规划含义很直接:每张 GPU 应该有一张 PIX 关系的网卡。这就是 rail-optimized 服务器 在硬件设计上"一卡配一网卡、且挂在同一个 PCIe switch 下"的原因—— L3 那节讲的是机间布线,这里是它在机内的前提。

!跨 NUMA 的代价是真实的

SYS 意味着数据要跨 CPU socket 间的互联(Intel 的 UPI、AMD 的 xGMI)。 后果有三个:带宽降低、延迟升高、而且会和其它跨 NUMA 流量互相干扰

所以 GPU 训练任务的进程绑核也要跟着拓扑走:让进程跑在与其 GPU 同 NUMA 的 CPU 上。 不过要注意 NCCL 的场合有个例外——nccl-tests 的实践里 mpirun -bind-to none 是常见配置,原因是过度绑核反而会干扰 NCCL 自己的线程调度。绑核这件事要实测,别照抄。

ACS:GPUDirect 的隐形开关

Access Control Services 是 PCIe 的一个安全特性:强制 peer-to-peer 流量绕到 Root Complex 去做检查,而不是让两个 Endpoint 在 switch 里直接对话。

对虚拟化是必要的(防止设备之间越权访问),但对 GPUDirect RDMA 是致命的:

ACS 开启:GPU ──► Switch ──► Root Complex ──► Switch ──► NIC   (绕远,且吃 CPU 侧带宽)
ACS 关闭:GPU ──► Switch ──► NIC                              (P2P 直通)

症状是静默退化:功能全对,带宽掉一截,日志里什么都不报。

# 查有哪些设备开了 ACS(ACSCtl 里的 SrcValid+ 等标志)
lspci -vvv | grep -i -A1 'Access Control Services'

# 确认 nvidia-peermem 已加载(GPUDirect RDMA 的前提)
lsmod | grep peermem

关 ACS 通常在 BIOS 里做,或者用启动参数。这和 IOMMU 是一组权衡:

需求ACSIOMMU
裸金属跑训练,要极致 P2P可关或按需
要把设备直通给虚机 / SR-IOV需要开(隔离要求)必须开

这是一道安全与性能的取舍题,不是"越关越好"。 多租户环境不要随便关。

检查清单

新机器到手,或者性能不对时按顺序过一遍:

# 1. 网卡协商到了预期的代际与宽度吗
for d in $(lspci | grep -i mellanox | cut -d' ' -f1); do
  echo "== $d"; lspci -vv -s $d | grep -E 'LnkCap:|LnkSta:'
done

# 2. GPU 与网卡的亲和关系是不是 PIX
nvidia-smi topo -m

# 3. 设备的 NUMA 归属
lspci -vv | grep -E 'Physical Slot|NUMA node' | paste - -

# 4. GPUDirect 的前提:peermem 模块
lsmod | grep peermem

# 5. 有没有设备开着 ACS
lspci -vvv | grep -c 'Access Control Services'

五条命令,两分钟。任何一条不对,后面测 NCCL 都是在测一个已知有问题的系统。

检查点

检查点单选

一张 400 Gb 网卡插在 PCIe Gen4 x16 槽位上,理论带宽上限约是多少?

检查点单选

lspci 显示某网卡 LnkCap 是 Speed 32GT/s Width x16,LnkSta 是 Speed 32GT/s Width x8 (downgraded)。说明什么?

检查点单选

为什么 GPUDirect RDMA 场景通常要求关闭 ACS?

这节课的落点

  • PCIe 是点对点交换网:Root Complex 是网关、Switch 是交换机、Endpoint 是终端
  • 带宽规律:每代翻倍、lane 线性;Gen5 x8 ≈ Gen4 x16 ≈ 31.5 GB/s
  • 看到 PCIe 带宽先问单向还是双向,这是最常见的 2 倍误差
  • 400G 网卡需要 Gen5 x16;插在 Gen4 x16 上上限只有约 250 Gb
  • LnkCap 是能力、LnkSta 是实际,(downgraded) 是白送的诊断信息
  • 拓扑距离 NV# < PIX < PXB < PHB < NODE < SYS,每张 GPU 应配一张 PIX 的网卡
  • SYS(跨 NUMA)要付带宽、延迟和干扰三重代价
  • ACS 开着会让 P2P 绕道 Root Complex,静默吃掉 GPUDirect 的收益;但它与 SR-IOV 的隔离需求是取舍
  • 测 NCCL 之前先跑那五条检查命令

延伸资料