多 PCIe Domain 与 NUMA 映射
基于双路 Sapphire Rapids (24 个 PCIe domain, 2 个 NUMA node)。详解 PCIe domain 与 NUMA 的对应关系、如何查询、以及跨 NUMA 访问 NVMe 的性能影响。
1. 背景:Sapphire Rapids 的 PCIe 架构
Intel Sapphire Rapids (SPR) 的 PCIe 架构与之前代际显著不同:
| 代际 | PCIe 拓扑 | 特点 |
|---|---|---|
| Ice Lake | 单 RC per socket, 多 Root Port | 所有设备共享同一 domain |
| Sapphire Rapids | 多 RC per socket | 每个 RC 有独立 domain number,设备分布更分散 |
每个 socket 有多个独立的 Root Complex,各自分配唯一的 domain number。这增加了拓扑复杂度——GPU 可能在 domain 97,NVMe 在 domain 7f。
2. 本环境 Domain 分布
# 列出所有 domain 及设备数
for d in /sys/devices/pci*; do
count=$(ls "$d" 2>/dev/null | grep -c "^0000:")
echo "$(basename $d): $count devices"
done
| Domain | 设备数 | 推测用途 | NUMA |
|---|---|---|---|
| 00 | 18 | 芯片组, VGA | — |
| 7e, 7f | 37, 122 | Socket 0 I/O, NVMe 阵列 | 0 |
| 97 | 5 | GPU (RTX 5090) | 1 |
| fe, ff | 37, 122 | Socket 1 I/O, NVMe 阵列 | 1 |
| 其他 (15/26/37/ek) | 4-8 | 系统外设 | 分布 |
3. 查询 Domain 的 NUMA 亲和性
3.1 sysfs 方法
# 方法 1: domain 的 numa_node(需内核支持)
cat /sys/devices/pci0000:97/numa_node 2>/dev/null
# 方法 2: 通过 domain 内任意设备查询
cat /sys/bus/pci/devices/0000:98:00.0/numa_node # GPU → 1
本环境 domain 级
numa_node返回空——内核版本未暴露。但设备级正常。
3.2 查询所有 domain 内的代表性设备
for d in /sys/bus/pci/devices/0000:7f:*/0000:* 0000:97:*/0000:* 0000:ff:*/0000:*; do
[ -d "$d" ] || continue
bdf=$(basename "$d")
cls=$(cat "$d/class" 2>/dev/null)
numa=$(cat "$d/numa_node" 2>/dev/null)
echo "$bdf | class=$cls | NUMA=$numa"
done 2>/dev/null | head -20
3.3 通过设备 BDF 规律推断
Sapphire Rapids 的 BDF 编码包含 NUMA 信息:
| BDF Pattern | Socket/NUMA |
|---|---|
0000:00:* ~ 0000:7f:* |
Socket 0 (NUMA node 0) |
0000:80:* ~ 0000:ff:* |
Socket 1 (NUMA node 1) |
GPU BDF = 0000:98:00.0 → bus 0x98 > 0x7f → Socket 1 → NUMA node 1。与 numa_node 返回的 1 一致。
3.4 NVMe 的 NUMA 位置
# 本环境 NVMe (3 块均在 domain d7 / Socket 1)
for nvme in /sys/block/nvme*/device; do
bdf=$(readlink -f "$nvme" 2>/dev/null | grep -oP "0000:[0-9a-f:]+" | head -1)
numa=$(cat /sys/bus/pci/devices/$bdf/numa_node 2>/dev/null)
echo "$(basename $(dirname $nvme)): $bdf, NUMA=$numa"
done
本环境所有 3 块 NVMe (nvme0-2n1) 均在 domain d7,与 GPU (domain 97) 同属 Socket 1。
这意味着:GPU 和 NVMe 在同一 NUMA node,GDS (GPU Direct Storage) 场景下数据路径全部在本地 socket 内——无需跨 UPI(Ultra Path Interconnect,Intel 双路 CPU 之间的点对点互连总线)。
4. 跨 NUMA PCIe 访问的性能影响
4.1 场景分析
Socket 0 (NUMA 0) Socket 1 (NUMA 1)
├── NVMe (domain 7f) ←?→ ├── GPU (domain 97)
└── NVMe (domain 7f) └── NVMe (domain d7) ← ✓ 本地
本环境 GPU 和 NVMe 同在 Socket 1 → GDS 无跨 NUMA 开销。
4.2 跨 NUMA 的 PCIe 路径
如果 GPU (Socket 1) 访问 Socket 0 的 NVMe (domain 7f):
NVMe (S0) → RC (domain 7f) → UPI → RC (domain 97) → Bridge → GPU (S1)
经过 UPI 跨 socket 链路,NUMA distance 21 vs 10,预计增加 ~2× 延迟。但由于 NVMe I/O 延迟本就 ~10-100 μs,额外的 NUMA 延迟 (~100ns) 影响很小。
5. PCIe Domain 与 GPU 编程
5.1 确认设备选对 domain
CUDA 的 cudaDeviceGetByPCIBusId 可确保选中特定 BDF 的 GPU:
int device_id;
cudaDeviceGetByPCIBusId(&device_id, "0000:98:00.0");
cudaSetDevice(device_id);
这在多 domain 环境中尤其重要——多张 GPU 可能分散在不同 domain。
5.2 Domain 与 DMA 可达性
DMA 通常可在同 domain 内自由进行。跨 domain 的 DMA(如 GPU 访问另一个 domain 的 NVMe)需要系统 firmware 的 ACPI DMAR 表支持,云环境可能受限。
6. 总结
| 问题 | 本环境答案 |
|---|---|
| GPU 在哪个 NUMA? | Node 1 (BDF 0x98 > 0x7f) |
| NVMe 在哪个 NUMA? | Node 1 (domain d7),与 GPU 同 socket |
| 有跨 NUMA PCIe 访问吗? | 本环境无——GPU 和 NVMe 在同一 socket |
| BDF 编码规则 | 0x00-0x7f = S0, 0x80-0xff = S1 |
| 多 domain 影响编程吗? | 需 cudaDeviceGetByPCIBusId 确保选对设备 |