背景#
想搞明白 RDMA 这东西,起因很简单。有一台 GPU 服务器做分布式训练,NVIDIA 文档里反复出现 InfiniBand 和 RoCE 这两个词,网卡选项里还有 ConnectX。不弄清楚选什么设备,几百万的 GPU 跑着跑着带宽瓶颈就来了。
RDMA(Remote Direct Memory Access)让一台机器直接读写另一台机器的内存,CPU 全程不参与数据搬运。这和传统的 send()/recv() 有什么本质区别?内核旁路和零拷贝。数据从应用 buffer 直接到网卡 wire,不经内核协议栈拷贝一次。在高频交易、AI 集群、分布式存储里,每微秒都算成本。
Linux 的 RDMA 支持由 rdma-core 和 libibverbs 提供。上层可以用两条路径:底层 libibverbs 手动管理 QP/MR/CQ,高层 librdmacm 帮你处理连接。往下有三种硬件实现:InfiniBand(专有网络)、RoCE(以太网跑 RDMA)、iWARP(TCP 上跑)。
核心原理#
四种关键对象#
RDMA 编程模型建立在四个对象上。每写一行 RDMA 代码,都绕不开它们:
Queue Pair(QP):通信的基本单元。一个 QP 包含一个 Send Queue 和一个 Receive Queue。你把 Work Request 投到队列里,硬件异步处理,完成后在 Completion Queue 里通知你。
Memory Region(MR):RDMA 硬件只能访问已注册的内存。ibv_reg_mr() 做了两件事:pin 住物理页防止换出,并把虚拟地址到物理地址的映射表交给网卡。注册后的 MR 有两个 key——lkey(本地访问)和 rkey(远程访问)。
Protection Domain(PD):安全隔离边界。同一个 PD 内的 QP 和 MR 可以互相访问,跨 PD 不行。类似进程地址空间的概念,但作用在 RDMA 资源上。
Completion Queue(CQ):Work Request 完成后,Completion Queue Entry(CQE)被硬件推到这里。轮询 CQ 是唯一的完成通知方式——没有中断,没有信号,纯 polling。
QP 状态机#
QP 不是创建完就能用的。它有一个严格的状态机:
| |
INIT 阶段配置本地属性,RTR 需要交换对端的 QP 信息(qp_num、LID/GID),RTS 完成后才能发数据。两个端点必须通过带外通道(通常是一条 TCP 连接)交换这些参数。这个带外交换是 RDMA 新手最容易卡住的地方。
InfiniBand vs RoCEv2#
| 维度 | InfiniBand | RoCEv2 |
|---|---|---|
| 网络层 | 专有 LRH + GRH | Ethernet + IP + UDP |
| 传输层 | BTH(相同) | BTH(相同) |
| 流控 | 硬件信用机制 | PFC + ECN |
| 路由 | Subnet Manager(SM)分配 LID | 标准 IP 路由 |
| 成本 | 专有交换机 + 线缆 | 标准以太网交换机 |
| 延迟 | ~1μs | ~2-3μs |
BTH(Base Transport Header)在两种协议里完全相同。差异全在低层:IB 用专有硬件做无损网络,RoCEv2 把 RDMA 报文塞进 UDP 封装(端口 4791),依赖 PFC 和 ECN 在以太网上模拟无损。
选型经验:自建集群、预算充足→InfiniBand。云上部署、已有以太网基础设施→RoCEv2。没有 RDMA 网卡还想学→Soft-RoCE(rxE),纯软件模拟,性能打折扣但零成本入门。
代码实战#
下面是一个完整的 RDMA Write 示例。Server 注册一块内存,Client 直接从远端写进去,Server 不需要 CPU 参与数据接收。
Server 端#
| |
Client 端#
| |
关键流程:Server 注册内存→带外交换地址→Client 构造 RDMA Write→ibv_post_send()→轮询 CQ。全程 Server 的 CPU 没有执行任何 recv() 调用。
生态现状#
RDMA 已经不是 HPC 的专属玩具。以下是实际在用 RDMA 的项目:
| 项目 | RDMA 用法 | 传输层 |
|---|---|---|
| PyTorch Monarch | 分布式训练参数同步,TorchStore 用 RDMA 做 tensor 跨节点搬运 | RoCEv2 |
| NCCL | GPU 间 AllReduce 通信,默认优先走 InfiniBand/RoCE | IB / RoCEv2 |
| Ceph | OSD 间数据复制,ms_async 后端支持 RDMA | InfiniBand |
| SPDK / NVMe-oF | NVMe over Fabrics,RDMA 做 target-initiator 传输 | RoCEv2 |
| TensorFlow | gRPC + RDMA 插件做分布式训练通信 | IB / RoCE |
| Apache Spark | Shuffle 阶段用 RDMA 加速数据交换 | RoCEv2 |
AI 训练集群是 RDMA 最大的消费场景。一台 8×H100 的节点,GPU 间用 NVLink(900GB/s),节点间靠 InfiniBand NDR400(400Gb/s)。没有 RDMA,千卡集群的通信开销能把 GPU 利用率从 90% 拖到 30%。
今日可执行动作#
- 搭 Soft-RoCE 环境。如果没有 RDMA 网卡,在你的 Linux 机器上装
rdma-core和libibverbs-dev,然后sudo modprobe rdma_rxe && sudo rdma link add rxe0 type rxe netdev eth0。上面两段代码可以直接跑。 - 跑 RDMA-Primer 示例。
git clone https://github.com/ManiAm/RDMA-Primer,从 step1 到 step7 逐层理解,每个程序都是自包含的。 - 用 ibv_devinfo 查看硬件。
ibv_devinfo -v列出所有 RDMA 设备、端口状态、速率。如果你有 ConnectX 网卡,看看link_layer是 IB 还是 Ethernet。

