cscyuge's blog

GPU 通信相关

January 23, 2026

GPU 通信相关

1. 硬件知识

1.1 网卡

测试的H20网卡型号:

[root@ys-h20-7k5h6y36-0000 ~]# lspci | grep -i eth
0000:2c:00.0 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0000:2c:00.1 Ethernet controller: Red Hat, Inc. Virtio network device (rev 01)
0000:2d:00.0 Ethernet controller: Red Hat, Inc. Virtio network device (rev 01)
0000:7f:00.0 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0000:7f:00.1 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0000:c7:00.0 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0000:c7:00.1 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0001:08:00.0 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0001:08:00.1 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0001:a2:00.0 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)
0001:a2:00.1 Ethernet controller: Mellanox Technologies MT43244 BlueField-3 integrated ConnectX-7 network controller (rev 01)

可以看到使用的BlueField-3 integrated ConnectX-7网卡。常见的网卡性能数据:

GPU型号 常见搭配网卡 网络速率(bps) 发布时间
V100 / T4 ConnectX-4/5 25G / 50G / 100G 2017–2018
RTX 3090 ConnectX-5 100G 2020
RTX 4090 ConnectX-5/6 100G / 200G 2022
L40S / 6000 Ada ConnectX-6 200G 2023
H20 ConnectX-6/7 200G / 400G 2023
A100 ConnectX-6 200G 2020
H100 ConnectX-7 400G 2022
H200 ConnectX-7/8 400G / 800G 2024
B200 ConnectX-8 800G 2024–2025
Grace Hopper GH200 ConnectX-7 400G 2023

在 InfiniBand / RDMA 架构里,把网卡(NIC)抽象为HCA(Host Channel Adapter),HCA可以给主机提供RDMA verbs 接口。

  • InfiniBand:一种物理层 + 链路层 + 网络层 + 传输层的完整网络协议(需要 InfiniBand 交换机和线缆)
  • verbs:一组用户态 API,类似于 Linux Socket API 定义了 TCP/UDP 通信方式,verbs API 定义了 RDMA 通信方式,API 的实现由 libibverbs 提供,底层由网卡驱动 (mlx5 内核模块) 处理。
    • libibverbs: Linux下的一个 用户态库,提供应用程序访问HCA的接口,就像 libc 实现了 open()/read()/write() 这些系统调用一样,libibverbs 实现了 ibv_post_send()/ibv_post_recv() 这些 verbs 调用。

查看一下HCA的信息:

[root@ys-h20-7k5h6y36-0000 bin]# ibv_devinfo | egrep "hca_id|port:"
hca_id: mlx5_bond_0
        transport:                      InfiniBand (0)
                port:   1
hca_id: mlx5_bond_1
        transport:                      InfiniBand (0)
                port:   1
hca_id: mlx5_bond_2
        transport:                      InfiniBand (0)
                port:   1
hca_id: mlx5_bond_3
        transport:                      InfiniBand (0)
                port:   1

可以看到有4个HCA设备,这些设备可以通过InfiniBand verbs API调用。

查看一下网口的信息:

[root@ys-h20-7k5h6y36-0000 draw]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:04:65:f9 brd ff:ff:ff:ff:ff:ff
5: reth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:59:10:bf brd ff:ff:ff:ff:ff:ff
6: reth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:59:10:bf brd ff:ff:ff:ff:ff:ff
7: reth2: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond1 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:3a:d3:58 brd ff:ff:ff:ff:ff:ff
8: reth3: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond1 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:3a:d3:58 brd ff:ff:ff:ff:ff:ff
9: reth4: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond2 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:10:79:b3 brd ff:ff:ff:ff:ff:ff
10: reth5: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond2 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:10:79:b3 brd ff:ff:ff:ff:ff:ff
11: reth6: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond3 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:4f:2a:01 brd ff:ff:ff:ff:ff:ff
12: reth7: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 9000 qdisc mq master bond3 state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:4f:2a:01 brd ff:ff:ff:ff:ff:ff
13: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:59:10:bf brd ff:ff:ff:ff:ff:ff
14: bond1: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:3a:d3:58 brd ff:ff:ff:ff:ff:ff
15: bond2: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:10:79:b3 brd ff:ff:ff:ff:ff:ff
16: bond3: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:25:9d:4f:2a:01 brd ff:ff:ff:ff:ff:ff
17: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:3c:24:24:2b brd ff:ff:ff:ff:ff:ff
19: vethd5122f5@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 
    link/ether 3e:ad:00:13:84:8d brd ff:ff:ff:ff:ff:ff link-netnsid 0

可以看到有lo, eth0, reth0, reth1, reth2, reth3, reth4, reth5, reth6, reth7, bond0, bond1, bond2, bond3这些网口。 lo表示本地环回网口,eth0表示物理网口,reth 表示 底层物理网口(Real Ethernet),bond 表示 绑定网口(Bonding)。

看一下HCA和网口的关系:

[root@ys-h20-7k5h6y36-0000 bin]# ibdev2netdev
mlx5_bond_0 port 1 ==> bond0 (Up)
mlx5_bond_1 port 1 ==> bond1 (Up)
mlx5_bond_2 port 1 ==> bond2 (Up)
mlx5_bond_3 port 1 ==> bond3 (Up)

mlx5_bond_0绑定了bond0,mlx5_bond_1绑定了bond1,mlx5_bond_2绑定了bond2,mlx5_bond_3绑定了bond3。

看一下bond0的信息:

[root@ys-h20-7k5h6y36-0000 draw]# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1.9

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer3+4 (1)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0
Peer Notification Delay (ms): 0

802.3ad info
LACP rate: slow
Min links: 0
Aggregator selection policy (ad_select): stable
System priority: 65535
System MAC address: 00:25:9d:59:10:bf
Active Aggregator Info:
	Aggregator ID: 1
	Number of ports: 2
	Actor Key: 65503
	Partner Key: 190
	Partner Mac Address: 00:00:5e:00:01:01

Slave Interface: reth0
MII Status: up
Speed: 200000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 5c:25:73:f7:b1:68
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned Count: 0
Partner Churned Count: 0
details actor lacp pdu:
    system priority: 65535
    system mac address: 00:25:9d:59:10:bf
    port key: 65503
    port priority: 255
    port number: 1
    port state: 61
details partner lacp pdu:
    system priority: 32768
    system mac address: 00:00:5e:00:01:01
    oper key: 190
    port priority: 255
    port number: 95
    port state: 61

Slave Interface: reth1
MII Status: up
Speed: 200000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 5c:25:73:f7:b1:69
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned Count: 0
Partner Churned Count: 0
details actor lacp pdu:
    system priority: 65535
    system mac address: 00:25:9d:59:10:bf
    port key: 65503
    port priority: 255
    port number: 2
    port state: 61
details partner lacp pdu:
    system priority: 32768
    system mac address: 00:00:5e:00:01:01
    oper key: 190
    port priority: 255
    port number: 395
    port state: 61

可以看到bond0聚合了reth0和reth1两个网口,每个物理网口的带宽为200Gbps。注意单个 TCP/UDP 流量 不会跨多条物理链路,它会固定走其中一条,bond0单个连接(单流)最大速率 = 200 Gb/s,多个连接(多流)并发时,理论总吞吐 ≈ 200 + 200 = 400 Gb/s。

但是GPUDirect RDMA 默认不会走 Linux bonding 哈希规则,它有自己的一套 多链路聚合机制:

  • InfiniBand 模式下,可以用 多链路带宽聚合 (Link Aggregation / Multipath),自动把多条物理 lane 当成一个逻辑大口。
    • lane:物理链路,每个物理链路可以承载一个 QP(Queue Pair)。
  • RoCE 下,可以用 ECMP 或 LAG,不同 QP(Queue Pair)可以分布到不同链路。
    • QP(Queue Pair):RDMA 通信的队列对,每个 QP 可以独立配置,包括发送队列和接收队列。
    • RoCE(RDMA over Converged Ethernet):不是一个完整的栈,而是 把 RDMA verbs 放到以太网上承载,底层还是以太网 (Ethernet) + IP 路由。
    • RoCE没有 IB 的 lane 聚合机制,取而代之的是:
      • ECMP(Equal Cost Multi-Path):等价多路径路由,在交换机层面,通过哈希算法将QP均匀分配到多条路径上。
      • LAG(Link Aggregation Group):链路聚合组,在 网卡侧(或交换机)将多条物理链路捆绑成一个逻辑链路,提高带宽和可靠性。
    • 如果一个 QP 流量特别大,使用RoCE可能只跑在一条链路上,不能像 IB 那样天然合并。所以在RoCE上想要打满带宽,需要多 QP 并行,配合 LAG/ECMP 才能分摊流量。

再来看一下mlx5_bond_0的具体信息:

[root@ys-h20-7k5h6y36-0000 ~]# ibv_devinfo -v
hca_id: mlx5_bond_0
        transport:                      InfiniBand (0) # verbs 层接口类型,通常写 InfiniBand,即使底层是 RoCE。
        ...
        max_qp:                         131072 # 支持的最大 QP 数量
        max_qp_wr:                      32768 # 单个 QP 的最大 work requests 数量(队列深度)。
        ...
        max_sge:                        30 # 单次请求最大 scatter/gather entries(即一次发送可拼接多少段内存)
        max_sge_rd:                     30
        max_cq:                         16777216 # 最大完成队列数
        max_cqe:                        4194303 # 每个 CQ 的条目数(队列深度)
        max_mr:                         16777216 # 支持的单个 Memory Region 最大注册内存大小
        ...
        atomic_cap:                     ATOMIC_HCA (1) # 网卡硬件支持 RDMA 原子操作(fetch-and-add, compare-and-swap)
        ...
        rc_odp_caps:
                                        SUPPORT_SEND
                                        SUPPORT_RECV
                                        SUPPORT_WRITE
                                        SUPPORT_READ
                                        SUPPORT_ATOMIC
                                        SUPPORT_SRQ # 支持从 GPU/CPU 的 page-fault 触发数据加载
        ...

        cq moderation caps:
                max_cq_count:   65535
                max_cq_period:  4095 us

        maximum available device memory:        65536Bytes

        num_comp_vectors:               63
                port:   1
                        state:                  PORT_ACTIVE (4)
                        max_mtu:                4096 (5)
                        active_mtu:             4096 (5) # MTU = 4096 字节,RoCE 默认大帧。越大越利于减少头部开销
                        sm_lid:                 0
                        port_lid:               0
                        port_lmc:               0x00
                        link_layer:             Ethernet # 真正的链路层,这里是 Ethernet → 表明走 RoCE。
                        max_msg_sz:             0x40000000
                        port_cap_flags:         0x04010000
                        port_cap_flags2:        0x0000
                        max_vl_num:             invalid value (0)
                        bad_pkey_cntr:          0x0
                        qkey_viol_cntr:         0x0
                        sm_sl:                  0
                        pkey_tbl_len:           1
                        gid_tbl_len:            255
                        subnet_timeout:         0
                        init_type_reply:        0
                        active_width:           2X (16) # 表示用两条 lane 聚合成 100G(可能是 2 × 50G SerDes)
                        active_speed:           100.0 Gbps (128) # 单端口单向速率 100Gb/s,全双工
                        phys_state:             LINK_UP (5)
                        GID[  0]:               fe80:0000:0000:0000:0225:9dff:fe59:10bf, RoCE v1
                        GID[  1]:               fe80::225:9dff:fe59:10bf, RoCE v2
                        GID[  2]:               0000:0000:0000:0000:0000:ffff:c812:000e, RoCE v1
                        GID[  3]:               ::ffff:200.18.0.14, RoCE v2
  • CQ moderation
    • HCA 完成一个 Work Request (WR) → 会生成一个 CQE (Completion Queue Entry) 放入 CQ。默认情况下,每个 CQE 都会触发一次 中断/通知,应用就能及时知道某个 WR 完成了。如果有成千上万条 WR,很快就会产生中断风暴,CPU 消耗严重。
    • 为了解决这个问题,引入了 CQ moderation:HCA 可以把多个 CQE 批量汇报,减少中断次数。
      • max_cq_count: CQ moderation 一次最多可以累积多少条 CQE,再统一触发一次通知。
      • max_cq_period: CQ moderation 最多能延迟多久才触发通知(就算 CQE 数没达到阈值)
  • Memory Region (MR)
    • RDMA中,网卡(HCA)可以 直接访问用户态内存(zero-copy),但前提是这块内存必须事先告诉网卡,保证网卡 DMA 时不会出错,这一步就是 注册 Memory Region (MR)。
    • Memory Region (MR) = 一段用户态内存,已经注册到 HCA,并获得一个 lkey/rkey,只有注册后的 MR,才能被 RDMA verbs(send, write, read, atomic)使用。
    • 注册MR流程:
      • 应用调用 ibv_reg_mr() 把内存注册成 MR。
      • HCA 驱动会 pin(固定)这段内存,防止它被换出。
      • 驱动把虚拟地址和物理页帧映射传给 HCA。
      • HCA 返回一个 lkey (local key) 和 rkey (remote key)。
        • lkey:本地访问时使用(本地 QP 引用这块内存)。
        • rkey:远端 RDMA 访问时使用(对方 QP 需要 rkey 才能写/读本地内存)。
  • Scatter/Gather Entry(SGE)
    • SGE描述的是一段 内存缓冲区(地址 + 长度 + lkey)。
    • 在 RDMA 里,一个 Work Request (WR) 里可以包含多个 SGE → 组成一个 SG List (Scatter/Gather List)。
    • 在实际应用中,数据可能并不是放在连续的内存里,而是分散在多个 buffer 中。
      • 没有 SGE:要先把这些 buffer 拷贝到一个连续内存里再发。
      • 有 SGE:可以直接把多个 buffer 的地址列出来(多个 SGE),网卡(HCA)会一次性 DMA 这些零散数据 → 零拷贝传输。
  • ODP (On-Demand Paging)
    • 在没有 ODP 的情况下,使用 RDMA 之前,你必须 提前注册一整段内存 (ibv_reg_mr),注册时,HCA 驱动会 pin(固定) 那些物理页,告诉 HCA 地址映射。
    • ODP 允许 HCA 在 访问内存时,遇到页不在内存里(page-fault),再由内核/驱动去处理
      • GPU 内存 ODP (GPUDirect ODP):HCA 发 DMA → 页还没映射到 GPU BAR/内存 → 触发 GPU page fault → CUDA driver/NVIDIA 内核模块建立映射 → HCA 再继续访问。
    • (推理场景没必要,一次性注册好让所有显存都可以被网卡访问就好了)

这里还可以看到链路层(link_layer)走的以太网(Ethernet),所以实际上用的RoCE,而不是一整套InfiniBand。

1.2 硬件总线上的拓扑结构

通过命令:

[root@ys-h20-7k5h6y36-0000 draw]# lstopo --of png > topo.png

可以获取到硬件总线上的拓扑结构:

topo

连线的数字代表链路带宽(GB/s),可以看到PCIe 64GB/s(双向)的理论值。

  • NUMA(Non-Uniform Memory Access):非一致性内存访问。在多路 CPU(多个物理 CPU 插槽,或 Chiplet 设计)系统中,每个 CPU(Socket)有自己直连的内存。本地 CPU 访问“自己插槽的内存”速度快;访问“别的 CPU 插槽的内存”需要跨 QPI/UPI/Infinity Fabric 链接,延迟更高,带宽更低。

也可以通过nvidia-smi topo -m 查看:

[root@ys-h20-7k5h6y36-0000 draw]# nvidia-smi topo -m
        GPU0    GPU1    GPU2    GPU3    GPU4    GPU5    GPU6    GPU7    NIC0    NIC1    NIC2    NIC3  CPU Affinity     NUMA Affinity   GPU NUMA ID
GPU0     X      NV18    NV18    NV18    NV18    NV18    NV18    NV18    NODE    NODE    SYS     SYS   0-47,96-143      0               N/A
GPU1    NV18     X      NV18    NV18    NV18    NV18    NV18    NV18    PIX     NODE    SYS     SYS   0-47,96-143      0               N/A
GPU2    NV18    NV18     X      NV18    NV18    NV18    NV18    NV18    NODE    NODE    SYS     SYS   0-47,96-143      0               N/A
GPU3    NV18    NV18    NV18     X      NV18    NV18    NV18    NV18    NODE    PIX     SYS     SYS   0-47,96-143      0               N/A
GPU4    NV18    NV18    NV18    NV18     X      NV18    NV18    NV18    SYS     SYS     PIX     NODE    48-95,144-191   1               N/A
GPU5    NV18    NV18    NV18    NV18    NV18     X      NV18    NV18    SYS     SYS     NODE    NODE    48-95,144-191   1               N/A
GPU6    NV18    NV18    NV18    NV18    NV18    NV18     X      NV18    SYS     SYS     NODE    PIX     48-95,144-191   1               N/A
GPU7    NV18    NV18    NV18    NV18    NV18    NV18    NV18     X      SYS     SYS     NODE    NODE    48-95,144-191   1               N/A
NIC0    NODE    PIX     NODE    NODE    SYS     SYS     SYS     SYS      X      NODE    SYS     SYS
NIC1    NODE    NODE    NODE    PIX     SYS     SYS     SYS     SYS     NODE     X      SYS     SYS
NIC2    SYS     SYS     SYS     SYS     PIX     NODE    NODE    NODE    SYS     SYS      X      NODE
NIC3    SYS     SYS     SYS     SYS     NODE    NODE    PIX     NODE    SYS     SYS     NODE     X

Legend:

  X    = Self
  SYS  = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
  NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
  PHB  = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
  PXB  = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
  PIX  = Connection traversing at most a single PCIe bridge
  NV#  = Connection traversing a bonded set of # NVLinks

NIC Legend:

  NIC0: mlx5_bond_0
  NIC1: mlx5_bond_1
  NIC2: mlx5_bond_2
  NIC3: mlx5_bond_3

通过拓扑图可以看到:

  • GPU0、1、2、3, NIC0、1在NUMA Node0上
    • NIC0离GPU1最近,数据链路走单一的PCIe bridge,在nvidia-smi中显示为PIX
    • NIC1离GPU3最近,数据链路走单一的PCIe bridge,在nvidia-smi中显示为PIX
    • GPU0、2与NIC0、1的数据链路走不同的PCIe Host Bridge之间的连接,在nvidia-smi中显示为NODE
  • GPU4、5、6、7,NIC2、3在NUMA Node1上
    • NIC2离GPU4最近,数据链路走单一的PCIe bridge,在nvidia-smi中显示为PIX
    • NIC3离GPU6最近,数据链路走单一的PCIe bridge,在nvidia-smi中显示为PIX
    • GPU5、7与NIC2、3的数据链路走不同的PCIe Host Bridge,在nvidia-smi中显示为NODE

如果GPU要跨NUMA Node使用NIC,就要走跨 Socket 的 NUMA 互联,在nvidia-smi中显示为SYS。

传输速度:PIX > PXB > PHB > NODE > SYS

路径类型 说明 Gen4 x16 理论带宽 Gen4 x16 常见实测 Gen5 x16 理论带宽 Gen5 x16 常见实测 典型延迟 (µs)
PIX 同一 PCIe switch 或最多 1 个 bridge 31.5 GB/s 26–30 GB/s 63.0 GB/s 48–58 GB/s 1–2
PXB 多级 PCIe bridge/交换,不经过 Host Bridge 31.5 GB/s 24–28 GB/s 63.0 GB/s 45–55 GB/s 1.5–2.5
PHB 穿过 PCIe Host Bridge (Root Complex) 31.5 GB/s 22–27 GB/s 63.0 GB/s 40–52 GB/s 2–3
NODE 同 NUMA,不同 Host Bridge 互联 31.5 GB/s 18–25 GB/s 63.0 GB/s 35–48 GB/s 4–6
SYS 跨 NUMA,经 QPI/UPI/InfinityFabric 31.5 GB/s 12–22 GB/s 63.0 GB/s 25–40 GB/s 8–15

可以通过perftest库测试不同路径的传输速度:

  • perftest库安装:
git clone https://github.com/linux-rdma/perftest.git
cd perftest
./autogen.sh
./configure
make -j$(nproc)
make install
  • perftest测试GPU-NIC带宽:
# 终端A(server)
ib_write_bw -d mlx5_bond_0 --use_cuda=4 -F -q 1 -n 1000 --report_gbits

# 终端B(client)
ib_write_bw -d mlx5_bond_0 --use_cuda=4 -F -q 1 -n 1000 --report_gbits <mlx5_bond_0_ip>

查看网卡ip:

[root@ys-h20-7k5h6y36-0000 perftest]# ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             172.19.77.153/20 fe80::216:3eff:fe04:65f9/64
reth0            UP
reth1            UP
reth2            UP
reth3            UP
reth4            UP
reth5            UP
reth6            UP
reth7            UP
bond0            UP             200.18.0.14/30
bond1            UP             200.18.0.30/30
bond2            UP             200.18.0.46/30
bond3            UP             200.18.0.58/30
docker0          UP             172.17.0.1/16 fe80::42:3cff:fe24:242b/64
vethd5122f5@if18 UP             fe80::3cad:ff:fe13:848d/64

测试得到NIC0(mlx5_bond_0)到各个GPU的带宽:

GPU id BW peak(Gbps) BW average(Gbps)
0 195.06 195.06
1 195.06 195.05
2 178.86 178.81
3 175.84 175.83
4 124.39 124.38
5 124.23 124.22
6 124.28 124.27
7 119.12 119.11

可以看到GPU1和NIC0的链路是PIX,带宽接近200Gbps的上限,为195.06Gbps。

GPU0 ↔ NIC0 被标为 NODE, 但这个 NODE 属于优质NODE ——CPU 内部互联(Ice Lake Mesh)足够宽 → 实测带宽接近 PIX。

其他 GPU (比如 GPU2/3) 虽然也是 NODE,但所在的 Root Complex 通道资源更有限 → 实测带宽低 10–15%。

2. RDMA

RDMA网络组成:

  • 应用层:应用一般通过ibverbs api与RDMA网卡交互
  • 传输层:寻址,RDMA 没有”IP 地址”和”MAC 地址”
  • 网络层:
    • InfiniBand:私有协议,比较贵
    • RoCE(RDMA over Converged Ethernet)
  • 数据链路层:网卡, 最常见的RDMA网卡是Mellanox的ConnectX系列网卡
GPU型号 常见搭配网卡 网络速率(bps) 发布时间
V100 / T4 ConnectX-4/5 25G / 50G / 100G 2017–2018
RTX 3090 ConnectX-5 100G 2020
RTX 4090 ConnectX-5/6 100G / 200G 2022
L40S / 6000 Ada ConnectX-6 200G 2023
H20 ConnectX-6/7 200G / 400G 2023
A100 ConnectX-6 200G 2020
H100 ConnectX-7 400G 2022
H200 ConnectX-7/8 400G / 800G 2024
B200 ConnectX-8 800G 2024–2025
Grace Hopper GH200 ConnectX-7 400G 2023

RDMA支持的操作:

  • 双边RDMA,需要通信双方的CPU参与:
    • RECV: 目标方准备接受消息
    • SEND: 发起方将信息发送给目标方
  • 单边RDMA,只需要发起方的 CPU 参与,目标方 CPU 并不知情
    • WRITE: 将数据从发起方的内存直接写入目标方的内存
      • WRITE_IMM: 类似 WRITE,将数据从发起方的内存直接写入目标方的内存,但额外地在目标方的完成队列(Completion Queue)中插入一个整数,用来通知目标方 CPU。
    • READ:直接从目标方的内存读取数据并写入发起方的内存
    • ATOMIC:对目标方的内存进行原子操作,如 Compare-and-Swap、Fetch-Add

— 来源:Chen Lequn 的博客

在 RDMA 中,一对通信节点被称为队列对(Queue Pair,QP)。类似于 IP 协议上常用的两种传输类型 TCP 和 UDP,RDMA 上有三种传输类型:

  • 可靠连接队列对(Reliable Connected Queue Pair, RC QP)
    • 基于连接,因此在通信之前需要先建立连接。通常来说连接数有限制。
    • 可靠传输,包含重传,保证消息送达顺序。
    • 可以一次性传输很大的消息,消息的大小可以远超 MTU。
    • 支持所有 RDMA 操作:RECV、SEND、WRITE、READ、ATOMIC
  • 不可靠连接队列对(Unreliable Connected Queue Pair, UC QP)
    • 类似 RC,但是传输不可靠,不保证消息送达,不保证消息送达顺序。
    • 只支持部分 RDMA 操作:RECV、SEND、WRITE
  • 不可靠数据报队列对(Unreliable Datagram Queue Pair, UD QP)
    • 无需建立连接。
    • 不保证消息送达,不保证消息送达顺序。
    • 消息必须小于 MTU
    • 只支持 RECV、SEND

— 来源:Chen Lequn 的博客

推理系统里面为了可靠通信一般都用的RC QP。

3. 高性能网络系统设计哲学

见libfabric的intro,感觉关键点一个是要做到零拷贝,还有就是要尽量减少和内核的交互。

3.1 TCP和RDMA

socket接口设计为了易用性,需要将用户态中的应用数据拷贝到内核提供的缓冲区中,多出来的这一次拷贝会影响传输性能。

为了避免多出来的这一次拷贝,可以让应用和网卡共享一段内存缓冲区,在发送数据时,网卡可以直接从内存读取数据;在接收数据时,网卡可以直接将数据写入到内存中;应用程序可以轮询(Poll)网卡的完成队列(Completion Queue)来知道网络操作是否完成,而这个轮询操作只不过是读取某段虚拟地址而已。 — 来源:Chen Lequn 的博客

TCP和RDMA

3.2 TCP和GPUDirect RDMA

GPUDirect RDMA:

当应用程序要发送数据的时候,用户态进程只需要告诉网卡这个消息所在的虚拟地址,网卡就能直接读取显存。值得注意的是,用户态进程在发送消息的时候,并不需要刻意区分到底这个虚拟地址是在 CPU 内存上的,还是在显卡的显存上的,因为内核已经提前设置好了 CPU、显卡以及网卡的页表,使得他们能够理解同一个虚拟地址空间。接收端也是类似的,网卡直接将数据写入显存,无需经过任何额外的复制。 — 来源:Chen Lequn 的博客

TCP和GPUDirect RDMA

4. libfabric

libfabric是一个RDMA通信库,应用调用libfabric的上层接口,具体的通信协议由不同的provider实现,provider包括tcp、udp、shm(共享内存)、verbs(RDMA ibverbs)、efa。

  • 为什么需要libfabric这样的通信库:NCCL、pytorch支持的集体通信(Collective Communication)需要建立全局的通信域,如果要动态地添加/删除通信节点(比如点对点传输kv cache),使用更上层的NCCL就需要先暂停整个集群的通信。
应用层 (MPI / NCCL / NVSHEMEM / ...)
       |
RDMA verbs / libfabric (统一编程接口)
       |
----------------------------------------
       |             |
   InfiniBand      RoCE (基于以太网)
 (专用网络栈)       (以太网封装)
       |
   Mellanox HCA (ConnectX-6/7, BlueField)
       |
   [可选优化: IBGDA = GPU 直接驱动 NIC 提交 RDMA 请求]
  • IBGDA(InfiniBand Global Data Access):是 NVIDIA GPU + Mellanox NIC 协作的一种机制。传统 RDMA 时,GPU 发起的通信需要 CPU 参与(比如由 CPU 提交 Work Queue Entry 到 NIC)。开启 IBGDA 后,GPU 内核可以直接通过 doorbell / BAR 映射 向 NIC 提交 RDMA 请求,实现 GPU 驱动 NIC,CPU完全绕过。

软件对象模型:

libfabric

  • Fabric: 所有硬件资源和软件状态的的集合,类似于用于保存全局状态的一个结构。
  • Domain: 类似于一张网卡,例如 eth0。一个 Fabric 可以拥有多个 Domain。
  • (EP) Endpoint: 数据收发的端点,一个 Domain 可以拥有多个端点,并且每个端点也有不同的类型。例如在同一张以太网卡上,可以监听多个 ip:port,可以使用 TCP 和 UDP 这样不同类型的协议。
  • (EQ) Event Queue:事件队列,用来报告控制平面操作的完成。
  • (CQ) Completion Queue: 完成队列,报告数据平面操作的完成(RECV / SEND / WRITE / READ)。CQ 属于 Domain,可以绑定到 Endpoint 上。
  • (AV) Address Vector:存储解析过的网络地址。需要先将通信的另一方加入 AV 才能对其发起通信。AV 属于 Domain,可以绑定到 Endpoint 上。
  • (MR) Memory Region:用于收发数据的缓冲区。无论是哪种 RDMA 操作,都需要指定 MR。注册 MR 需要经过操作系统内核,因为操作系统内核需要设置 CPU 页表以及其他 PCIe 设备的页表。 — 来源:Chen Lequn 的博客

5. NVSHMEM与NCCL (待补充)

6. DeepEP (待补充)

7. MoonCake与NIXL (待补充)

参考链接