2. PCIe 拓扑结构:根复合体、交换器、端点、桥接器

在动手调吞吐之前,得先把 PCIe 的“骨架”摸清楚。说白了,拓扑就是设备之间怎么连的。

我刚接触 PCIe 时,以为所有设备都直接挂在 CPU 上。直到第一次排查吞吐瓶颈,才发现问题出在 Switch 内部。

2.1 四个核心角色

PCIe 拓扑里,角色很清晰。每个设备都有明确的定义。

组件 英文 一句话
根复合体 Root Complex (RC) CPU 的门户,所有请求的起点
交换器 Switch 内部数据,扩展端口用
端点 Endpoint (EP) 实际的业务设备,比如 GPU、NVMe SSD
桥接器 Bridge 连接其他总线(比如 PCI/PCI-X)
我的习惯:拿到一块新板卡,先翻原理图,确认 RC 和 Switch 之间的通道数。很多时候性能不达标,就是这个链路本身不够宽。

2.2 根复合体 (Root Complex)

RC 就是 CPU 侧的总指挥。它负责将 CPU 和内存的请求,转发到 PCIe 总线上去。

我个人习惯把 RC 想象成机场的出发大厅。所有登机手续(事务层报文)都得从这里办。

  • 任务:生成配置请求、内存请求、I/O 请求。
  • 位置:通常集成在 CPU 或芯片组内部。
  • 吞吐关键:RC 端的入站/出站地址翻译缓冲区的大小。如果这个缓冲区太小,在高吞吐时容易丢包。
注意:我曾经调试过一个 case,吞吐始终跑不满 x16 的带宽。最后发现是 RC 的 MSI-X 中断分配不合理,导致中断风暴。这不是硬件故障,是 RC 端资源管理的问题。

2.3 交换器 (Switch)

Switch 就是拓扑里的“立交桥”。它把一根 PCIe 链路,扩展成多根。记住,它不参与业务,只负责转发。

  • 内部架构:一个上行端口 + 多个下行端口。
  • 转发方式:基于报文头里的 BDF(总线:设备:功能)号。
  • 吞吐瓶颈:内部仲裁逻辑。多端点同时向主机写数据时,如果仲裁费时太久,就会产生气泡。

为什么 Switch 会吃掉延迟?

因为每个报文都要经过内部虚拟通道(VC Buffer)排队。端口越多,竞争越激烈。所以,能用直连就别过 Switch。

2.4 端点 (Endpoint)

端点是最常见的。我们调吞吐,其实就是在调端点和 RC 之间的数据交换效率。

  • 类型 A:普通 EP,比如网卡、存储控制器。
  • 类型 B:根复合体集成端点,比如 CPU 内部集成的 GPU(不过这种少见)。
  • 关键属性:DMA 能力、BAR 空间大小、是否支持 ARI(替代路由ID解释)。
避坑指南:我曾见过一个 NVMe SSD 在连续写入时速度断崖式下跌。检查发现该 EP 的 Max Payload Size 只设了 128 字节。改成 512 字节后,吞吐提升 40%。很多时候,性能不是架构的问题,是端点配置没对齐。

2.5 桥接器 (Bridge)

桥接器是“翻译官”。它让 PCIe 可以兼容老旧的 PCI/PCI-X 设备。

说句实在话,在纯 PCIe 环境里,桥接器出现得很少。但如果你的系统里真的有,那就得注意了。

  • 作用:协议转换、地址转换。
  • 吞吐影响:桥接器会引入额外的处理延迟。因为要拆包、翻译、再组包。通常吞吐会打 7-8 折。

你觉得什么样的拓扑结构对吞吐最友好?我个人认为,越扁平越好。

2.6 典型拓扑结构

下面是我总结的几种常见拓扑,以及它们对性能的影响。

拓扑类型 描述 吞吐特性
直连拓扑 EP 直接连在 RC 上,不经过 Switch 延迟最低,吞吐最高
单 Switch 树形 RC 连一个 Switch,Switch 下挂多个 EP 共享上行带宽,多 EP 同时写时需注意
多 Switch 级联 Switch 下面再连 Switch 延迟增加,吞吐受制于最窄链路
非透明桥 (NTB) 两个系统通过桥连接 多用于多主机通信,吞吐取决于桥能力
PCIe 典型拓扑结构图 根复合体 (RC) Switch (上行/下行) 端点 (EP1) - GPU 端点 (EP2) - NVMe Switch (级联) 端点 (EP3) - 网卡 说明:红色与绿色端点直连 Switch,共享上行 x16 带宽。 级联 Switch 引入额外延迟,适用于非实时业务。
我的建议:画拓扑图时,别只看物理连接。更要看逻辑链路。比如 EP 支持 ARI(替代路由ID解释),就能减少 Switch 内部的地址查找步骤。很多低延迟设计都在这些细节里。

2.7 拓扑对吞吐的影响

说完了结构,聊聊实实在在的影响。我总结为三点:

  1. 共享瓶颈:多端点通过一个 Switch 上行端口,总吞吐不会超过上行链路的速率。比如上行是 PCIe 4.0 x8(~16 GB/s),那所有端点加起来就这么多。
  2. 内部 Congestion:Switch 内部的交叉开关和 Virtual Channel 仲裁。如果多端点同时发送大块写请求,内部排队会导致延迟增加,从而降低有效吞吐。
  3. 地址转换开销:在 NTB 或桥接器场景里。地址转换成本不可忽略。我曾经测过,一次 NTB 地址翻译要额外消耗 200 ns。每秒百万次事务,累积下来就是几百毫秒的延迟。
我曾经踩过的坑:在调试一个多 GPU 服务器吞吐时,发现两个 GPU 之间的通信速率只有理论值的 60%。查拓扑发现 GPU 1 挂 Switch A,GPU 2 挂 Switch B,而 Switch A 和 Switch B 之间只有一个 x4 的级联口。数据得绕一大圈。解决方案是改拓扑,让两个 GPU 都挂在同一个 Switch 下,或使用 GPU Direct RDMA 绕过主机内存。

所以,每次做性能优化,第一步就是看拓扑图。确认从 EP 到 RC 之间,每一跳的链路宽度和协议版本。这一步看不出来,后面的优化就是盲人摸象。