RDMA 远程直接内存访问技术

高性能网络 2026-07-12 · 阅读约 23 分钟

当数据中心网络进入 100G/200G/400G 时代,一个尴尬的事实浮出水面:网卡比软件快得多。40G 网络的理论包间隔已小于一微秒,而传统 TCP 路径上一次系统调用加几趟内存拷贝就要花掉十几微秒——CPU 成了瓶颈,网络反而不是。RDMA(Remote Direct Memory Access)的答案是把三件事做到极致:零拷贝(数据不经过任何中间缓冲)、内核旁路(数据面完全不过内核协议栈)、对端零 CPU(单边操作直接读写远程内存)。本文拆解 RDMA 的架构与 verbs 编程模型,并用一段零依赖 Python 模拟把三条收益量化出来。

一、传统 TCP 路径的成本解剖

用 socket 发送一块 64KB 数据,数据与 CPU 分别经历了什么:

  1. 应用缓冲 → 内核套接字缓冲:send() 系统调用,一次上下文切换 + 一次内存拷贝;
  2. TCP/IP 协议栈处理:分段、序号、校验和、拥塞控制、定时器——全部消耗 CPU 指令;
  3. 内核缓冲 → 网卡:DMA 搬运;
  4. 接收方反向重演:网卡中断 → 软中断协议栈处理 → recv() 系统调用 → 拷贝到应用缓冲。

合计 3 次拷贝、2 次系统调用、1 次中断、大量协议栈指令。更糟的是这些成本随消息大小线性增长——带宽越大的机器,CPU 被网络吃得越多。

二、RDMA 的架构:把数据面整个搬进网卡

RDMA 网卡(RNIC,如 Mellanox/NVIDIA ConnectX 系列)自己实现了完整的有序可靠传输协议,工作方式:

  • 内存注册(Memory Registration):应用预先把要收发的内存区域注册给网卡,页被钉住(pin)防止换出,网卡拿到翻译表可以直接 DMA 访问。这是一次性成本,之后这块内存的收发完全不需要 CPU;
  • 队列对(Queue Pair, QP):通信端点是发送队列 + 接收队列的组合。应用把工作请求(WR)提交到队列,写一下门铃寄存器通知网卡——整个过程是几十条指令的用户态操作,无系统调用;
  • 网卡硬件接管:网卡从发送队列取请求,直接从注册内存 DMA 数据、切包、加协议头、发出;对端网卡收到后直接 DMA 进对端注册内存,完成队列(CQ)里放一条完成记录。两端 CPU 在数据面全程零参与。

三、单边 vs 双边:RDMA 的标志性语义

  • 双边操作(SEND/RECV):类似消息传递,发送方 SEND 需要接收方预先挂出 RECV 缓冲区,两端 CPU 都参与(虽然都不拷贝数据);
  • 单边操作(READ/WRITE/ATOMIC):本端直接读或写对端的注册内存——对端 CPU 完全不参与,甚至不需要知道这次访问发生了。这是『远程直接内存访问』名称的本义,也是 RDMA 与传统消息传递最本质的区别。

单边语义改变的是系统设计的思维方式:远程内存可以像本地内存一样被寻址。分布式共享内存、远程持久化数据结构(远端 B+Tree、哈希表)、无 CPU 开销的存储副本写入,都建立在单边 READ/WRITE 之上。单边 ATOMIC(compare-and-swap、fetch-and-add)则提供了无锁的远程同步原语。

四、动手实验:三条收益的量化对比

下面代码用成本模型模拟同机房 64KB 消息在两种路径上的传输:统计数据拷贝次数、CPU 参与指令数与端到端延迟,并演示单边 READ 的对端零开销。零依赖,Python 3 直接运行:

SIZE = 64 * 1024            # 64KB 消息
CPU_CYCLE_NS = 0.33         # 3GHz 每周期
COPY_BYTES_PER_CYCLE = 32   # AVX 每周期拷贝 32B
SYSCALL_NS = 1500           # 系统调用 ~1.5us
IRQ_NS = 2000               # 中断处理 ~2us
DMA_NS = 8000               # 网卡 DMA 搬运 64KB
WIRE_NS = 5000              # 网线 ~5us

def sim_tcp():
    copies, cpu_ns, instr = 0, 0.0, 0
    copies += 1                       # 应用 -> 内核缓冲
    cpu_ns += SYSCALL_NS
    instr += SIZE // COPY_BYTES_PER_CYCLE
    copies += 1                       # 内核 -> 网卡(协议栈处理)
    instr += SIZE // 64               # TCP 分段/校验和等
    cpu_ns += IRQ_NS + SYSCALL_NS     # 对端中断 + recv
    copies += 1                       # 内核 -> 应用
    instr += SIZE // COPY_BYTES_PER_CYCLE
    cpu_ns += instr * CPU_CYCLE_NS
    return copies, cpu_ns, instr, cpu_ns + DMA_NS + WIRE_NS

def sim_rdma():
    instr = 50                        # 仅提交工作请求: 写门铃
    cpu_ns = instr * CPU_CYCLE_NS
    return 0, cpu_ns, instr, cpu_ns + DMA_NS + WIRE_NS

在沙箱(Python 3,无第三方库)上的真实运行输出:

消息大小: 64KB, 同机房链路

== 数据拷贝次数 ==
  TCP 路径 : 3 次 (应用->内核->网卡->内核->应用)
  RDMA 路径: 0 次 (注册内存由网卡直接 DMA, 零拷贝)

== CPU 参与 ==
  TCP 路径 : 5120 条指令, CPU 时间 6.7us
  RDMA 路径: 50 条指令, CPU 时间 0.02us
  CPU 开销比: 405x

== 端到端延迟模型 ==
  TCP : 19.7us
  RDMA: 13.0us
  延迟比: 1.5x

== 单边语义演示: 远程读 ==
  TCP 模型: 读对端数据 = 对端必须运行 recv/send, 消耗对端 CPU
  RDMA READ: 本端网卡直接从对端注册内存 DMA, 对端 CPU 指令数 = 0
  -> 对端甚至可以完全不知情(数据被读), 这是"远程直接内存访问"的本义

校验通过: RDMA 零拷贝 / CPU 开销降两个数量级 / 延迟显著更低

五、结果解读:数量级差异在哪里

  • CPU 开销 405 倍是最本质的差距:TCP 的 5120 条指令里大头是数据拷贝(每 32 字节一条)与协议栈处理(分段、校验和、拥塞控制状态机);RDMA 把这些全部卸载到网卡硬件,CPU 只剩提交工作请求的 50 条指令。真实场景里一台跑满 100G TCP 的机器要把十几个核喂给网络栈,RDMA 下这些核全部还给业务。
  • 拷贝次数 3 → 0:注意 RDMA 的零拷贝有一个前提——内存必须先注册(页钉住 + 地址翻译表交给网卡)。注册是慢操作,所以高性能应用都用预注册的内存池复用,而不是每次传输临时注册。
  • 延迟 1.5 倍在本模型里看起来温和,因为模型简化了协议栈排队与调度抖动;真实世界里 TCP 的尾延迟(P99)受中断合并、软中断调度影响常常抖到数百微秒,而 RDMA 的 P99 延迟稳定在 10 微秒量级——尾延迟的确定性才是低延迟系统最看重的指标。
  • 单边读的零对端开销:实验中 TCP 模型读远程数据要求对端 CPU 跑完整 recv/send 循环,RDMA READ 则完全绕开对端。这把『读远程数据』的成本从『两台机器的 CPU』变成『一台机器的网卡』,是分布式内存池化的技术基石。

六、承载协议:InfiniBand、RoCE 与 iWARP

  • InfiniBand:RDMA 的原生载体,链路层就保证无损(credit-based 流控),性能最好但需要专用交换机和子网管理器,成本与生态封闭是门槛;
  • RoCE(v1/v2):RDMA over Converged Ethernet,跑在标准以太网上。RoCEv2 封装在 UDP/IP 里可以路由,是数据中心主流;关键前提是无损以太网——需要 PFC(优先级流控)+ ECN 拥塞管理,配置复杂是主要运维负担;
  • iWARP:RDMA over TCP,可容忍有损网络但实现复杂度高、延迟略逊,市场份额很小;
  • Soft-RoCE:纯软件实现的 RoCE,任何网卡都能跑 RDMA verbs 语义,是学习和测试的利器(本文概念均可先在 Soft-RoCE 上验证)。

七、工程应用

  • AI 训练:NCCL 在 IB/RoCE 网络上自动走 GPUDirect RDMA——GPU 显存之间直接搬运梯度,CPU 与系统内存完全不参与,这是大规模训练线性扩展比的通信基础(本站《NCCL 集合通信原理解析》会展开);
  • 分布式存储:NVMe-oF(NVMe over Fabrics)用 RDMA 把远程 NVMe 盘的访问延迟压到接近本地盘;Ceph、DAOS 的高性能数据面均基于 RDMA;
  • 内存数据库:FaRM(Microsoft)用单边 READ 构建分布式内存事务,单集群吞吐上亿次操作/秒,是 RDMA 系统设计的里程碑论文;
  • 解耦架构:内存解耦(memory disaggregation)把内存池化成独立资源,计算节点通过 RDMA 按需挂载远程内存,是数据中心资源利用率的下一战场。

八、常见误区

  1. 以为买了 RDMA 网卡就有性能:没有 PFC/ECN 配置的有损以太网上,RoCE 丢包重传的代价会让性能比 TCP 还差;无损网络配置是前置工程,不是可选项。
  2. 每次传输临时注册内存:注册操作涉及页表钉住与 IOMMU 映射,开销以微秒计且随大小增长;正确做法是启动时建好内存池反复复用。
  3. 把单边 WRITE 当消息用:单边写不通知对端,对端必须自己轮询内存标志位或等完成事件——用单边语义做通知需要精心设计内存序与可见性,否则就是数据竞争的重灾区。
  4. 忽视 QP 数量与连接拓扑:全互联集群的 QP 数量是 N² 量级,每个 QP 占网卡缓存,大规模集群必须用共享接收队列(SRQ)、动态连接传输(DCT)或 XRC 控制连接规模。
  5. 用 TCP 的编程直觉写 verbs 程序:verbs 是异步完成队列模型(提交 WR → 轮询 CQ),把同步 socket 思维硬套上去会丢掉大半性能;新接触者建议从 perftest(ib_write_bw 等)与 UCX/libfabric 等高层封装起步。

九、参考文献

  • Dragojević, A. et al. FaRM: Fast Remote Memory. NSDI 2014.
  • Kalia, A., Kaminsky, M., Andersen, D. Design Guidelines for High Performance RDMA Systems. USENIX ATC 2016.
  • Mellanox/NVIDIA. RDMA Aware Networks Programming User Manual (verbs API). docs.nvidia.com/networking.
  • Guo, C. et al. RDMA over Commodity Ethernet at Scale (RoCE at Microsoft). SIGCOMM 2016.
  • Shan, Y. et al. LegoOS: A Disseminated, Distributed OS for Hardware Resource Disaggregation. OSDI 2018.