为什么需要 RDMA:内核旁路与零拷贝
同样的 100Gbps 网卡,TCP 与 RDMA 的延迟差一个数量级。差在哪里,代价是什么。
学完这节你能做到
- 说清 TCP 路径上哪几步被 RDMA 省掉了
- 解释 QP、CQ、MR、Verbs 这几个基本概念
- 判断一个业务该不该上 RDMA
建议先学跳过这几节会看不懂本节的部分推导
先算一笔 CPU 的账
100 Gbps 意味着每秒要搬 12.5 GB 数据。走标准 TCP 路径,每个包都要:
- 至少一次内存拷贝(用户态 ↔ 内核态)
- 一次协议栈处理(分段、校验、状态机)
- 参与中断与软中断调度
- 唤醒等待的进程,一次上下文切换
经验数据:内核 TCP 每 1 Gbps 大约要吃掉 1 GHz 左右的 CPU。跑满 100 Gbps 就是好几个核只用来搬数据, 而这些核本来该用于跑业务或者喂 GPU。
到了 400 Gbps 的量级,这条路直接走不通了。这才是 RDMA 存在的理由——不是"快一点", 而是在这个速率上,让 CPU 参与搬运本身已经不可行。
RDMA 省掉了什么
同一条物理链路,把内核整段摘出去之后剩下什么。
对比两条路径,RDMA 省掉的正是 TCP 路径上最贵的三样:
| 省掉的东西 | TCP 路径上的代价 |
|---|---|
| 内存拷贝 | 每个包至少一次拷贝,占内存带宽 |
| 内核协议栈 | 分段、校验、状态机、netfilter |
| 上下文切换 | 系统调用 + 进程唤醒 |
剩下的路径极短:应用把请求写进队列 → 网卡自己 DMA 取数据 → 硬件封装发出 → 对端网卡直接落进内存。 全程 CPU 没有参与搬运。
延迟量级的差别:
| 路径 | 典型单向延迟 |
|---|---|
| 内核 TCP(同机房) | 20–50 μs |
| RDMA(IB 或 RoCE) | 1–3 μs |
- 内核旁路(kernel bypass):控制路径不走系统调用,用户态直接敲网卡的门铃寄存器
- 零拷贝(zero copy):数据路径上网卡直接读写应用注册过的内存,不经过中转缓冲
两者都省,但省的是不同的开销。DPDK 只做到了内核旁路(数据仍要 CPU 轮询搬运), RDMA 两个都做到了 —— 这是它延迟能低到个位数微秒的原因。
五个基本对象
用 RDMA 之前必须认识这几个名词,后面所有报错都围绕它们:
| 对象 | 全称 | 是什么 |
|---|---|---|
| QP | Queue Pair | 一对发送/接收队列,相当于 RDMA 里的"连接" |
| CQ | Completion Queue | 完成队列,操作干完了在这里通知你 |
| MR | Memory Region | 注册过的内存区域,网卡才有权限访问它 |
| PD | Protection Domain | 保护域,把一组 QP 和 MR 圈在一起做权限隔离 |
| Verbs | — | 用户态 API(libibverbs),所有上层库都建在它上面 |
MR 是最容易出问题的一个。 注册内存意味着把它锁住(pinned),不允许被换出, 否则网卡拿着的物理地址就失效了。所以:
# 锁内存额度不足时应用会在初始化阶段就失败
ulimit -l # 期望 unlimited
在容器里跑 RDMA 应用必须给 IPC_LOCK 能力,否则锁不了内存:
securityContext:
capabilities:
add: ["IPC_LOCK"]
症状是应用一启动就报 Cannot allocate memory 或 ibv_reg_mr failed,
而机器上明明有大把空闲内存。原因是锁内存额度不够,不是内存不够。
容器里还要额外加 IPC_LOCK。这两件事查一次记一辈子。
单边操作:对端 CPU 完全不知情
这是 RDMA 最反直觉的地方:
| 类型 | 操作 | 对端 CPU 参与吗 |
|---|---|---|
| 双边 | SEND / RECV | 要,对端必须先挂好 RECV 请求 |
| 单边 | WRITE / READ | 不参与,网卡直接读写对端内存 |
WRITE 的含义是"我把数据直接写进你那块内存",对端应用毫不知情, 也不需要被中断打扰。这就是分布式存储和 AI 训练能榨出极限性能的关键: 接收方的 CPU 可以一直忙自己的事。
代价是编程模型变了:双方要先交换内存地址和访问密钥(rkey), 谁在什么时候读写哪块内存都得由上层协议约定好。所以业务代码几乎不会直接写 Verbs, 而是用上层库:NCCL(AI 集合通信)、UCX、libfabric、NVMe-oF。
三种传输方式
| 方式 | 底层 | 说明 |
|---|---|---|
| InfiniBand | 自己的二层三层 | 链路层 credit 流控,天生无损;需要 SM 与专用交换机 |
| RoCEv2 | UDP over IP | 能被普通三层网络路由;需要自己把网络配成无损 |
| iWARP | TCP | 靠 TCP 保证可靠,部署简单但生态小,实际很少见 |
现在的实际选择基本是前两个。它们的性能差距不大,差距在运维: IB 的无损是网络自带的,RoCE 的无损要靠人配 PFC 和 ECN——配错了比不配还糟。 下一节专门讲这件事。
代价清单
RDMA 不是免费的加速开关。上之前要认下面几笔账:
- 网络必须无损(RoCE 场景):PFC / ECN 配错会全网卡顿,排查难度远高于 TCP
- 排障工具全换一套:
tcpdump抓不到 RDMA 的数据流(它绕过了内核), 要用ibdump、perfquery、网卡计数器 - 内存要注册、要锁住:容器里还要额外给权限
- 应用要适配:不能靠改配置获得,得用支持 RDMA 的库
- 硬件绑定:驱动(DOCA OFED)、固件、交换机版本要成套匹配
- 业务在用支持 RDMA 的库吗(NCCL / MPI / NVMe-oF / GPFS)?如果应用只会用 socket,上了也用不到。
- 瓶颈真的是网络吗?先量一遍:如果 CPU 打满在业务逻辑上、或者磁盘先撞墙,网络换成 RDMA 也没用。
- 团队有人能调 PFC/ECN 吗(RoCE 场景)?没有的话,要么选 IB(网络自带无损), 要么先用高带宽 TCP 把业务跑起来。
AI 训练是唯一几乎不用犹豫的场景 —— 多机多卡的梯度同步就是纯粹的网络问题,NCCL 也天然支持。
检查点
RDMA 相比内核 TCP,主要省掉了哪些开销?(多选)
容器里的 RDMA 应用启动即报 ibv_reg_mr failed,但节点有大量空闲内存。最可能的原因?
关于 RDMA WRITE(单边操作),下面哪个说法正确?
这节课的落点
- 内核 TCP 大约每 1 Gbps 吃 1 GHz CPU;到 400G 量级,让 CPU 搬数据这条路本身走不通
- RDMA = 内核旁路 + 零拷贝,延迟从几十微秒降到个位数微秒
- 五个基本对象:QP、CQ、MR、PD、Verbs;MR 必须锁内存,容器里要给
IPC_LOCK - 单边操作(WRITE/READ)对端 CPU 完全不参与,这是性能的关键来源
- IB 天生无损,RoCEv2 要自己把以太网配成无损,性能接近但运维成本差很多
- 上 RDMA 前先确认三件事:应用是否支持、瓶颈是否真在网络、团队能否调无损网络
延伸资料
- ·Systems Performance, 2nd Edition — Brendan Gregg
- ·k8s-in-action
network/network-operator/README.md - ·Storpath 存储运维成长路径 ↗