RDMA硬件基础:InfiniBand与RoCEv2架构对比

各位同学,今天我们来聊聊RDMA的硬件基础。说实话,这部分内容我当年刚接触时也觉得挺枯燥的,但后来在实际项目中踩过几次坑之后,才真正体会到——不理解硬件,上层优化就是空中楼阁。

RDMA的核心思想很简单:让数据绕过CPU,直接在内存之间搬运。但具体怎么实现?这就得看底层的网络架构了。目前主流的两条路:InfiniBand和RoCEv2。我个人的习惯是,先搞清楚它们各自的脾气,再决定用哪个。

InfiniBand:专为高性能而生

InfiniBand(简称IB)从一开始就是为HPC设计的。它有一套完整的协议栈,从物理层到传输层全是自家的。你想想看,这意味着什么?意味着它不需要像TCP/IP那样层层封装,延迟自然就低。

我在项目中遇到过最极端的情况:用IB做MPI通信,单边延迟能压到1微秒以内。而同样的应用,换成TCP/IP,延迟直接飙到10微秒以上。这就是专用硬件的优势。

IB的架构里有个关键角色——子网管理器(Subnet Manager)。它负责整个IB网络的拓扑发现、路由计算和地址分配。嗯,这里要注意:IB网络里没有IP地址那一套,用的是LID(Local Identifier)。

核心特点:

  • 端到端流控:硬件级别的信用机制,不会丢包
  • 原子操作:支持Fetch-and-Add、Compare-and-Swap等
  • 多路径传输:自动负载均衡
  • 高带宽:目前单端口可达400Gbps

RoCEv2:在以太网上跑RDMA

RoCEv2(RDMA over Converged Ethernet version 2)说白了就是把RDMA封装在UDP/IP里,跑在标准以太网上。为什么会有这东西?因为IB网卡太贵了,而且需要专门的交换机。很多数据中心不想为了RDMA单独拉一套网络。

RoCEv2的架构很有意思:它用UDP的源端口做负载均衡,用IP地址做路由。但问题来了——以太网本身是会丢包的。RDMA最怕丢包,一旦丢包,性能直接崩盘。

我曾经在一个项目中调试RoCEv2的性能,发现带宽死活上不去。查了半天,原来是交换机上的PFC(优先级流控)没开。开了之后,带宽从10Gbps直接跳到90Gbps。所以啊,RoCEv2对网络的要求其实很高,需要整个网络链路都支持无损特性。

避坑指南:我曾经在混合部署IB和RoCEv2的集群上踩过坑。两种网络的寻址方式完全不同,IB用LID,RoCEv2用GID(基于IPv6)。如果程序里写死了地址格式,换网络环境就崩。建议一开始就做好抽象层。

RNIC工作原理:数据是怎么飞起来的

RNIC(RDMA Network Interface Controller)就是RDMA网卡。它可不是普通的网卡,它有自己的处理器、内存和DMA引擎。说白了,它就是一块智能网卡。

RNIC的工作流程大致是这样的:

  1. 应用程序注册内存区域(注册到RNIC的页表中)
  2. 创建QP(队列对)和CQ(完成队列)
  3. 通过Work Request(WR)提交任务
  4. RNIC直接从用户态内存读取数据,封装成报文发出去
  5. 对端RNIC收到报文,直接DMA到目标内存
  6. 完成通知通过CQ返回

整个过程,CPU只在最开始和最后阶段参与一下。中间的数据搬运,全是RNIC自己干的。这就是RDMA性能好的根本原因。

个人经验:我建议你在写RDMA程序时,把内存注册和QP创建放在初始化阶段。别在热路径上做这些操作,否则延迟会高一个数量级。我见过有人每发一次消息就注册一次内存,结果性能还不如TCP。

GID与LID寻址机制

寻址这块,IB和RoCEv2走的是完全不同的路子。

LID:IB世界的门牌号

LID是16位的本地标识符,由子网管理器分配。每个IB端口至少有一个LID,范围是0x0001到0xBFFF。LID只在子网内部有效,跨子网通信需要GRH(Global Routing Header)。

我记得第一次配IB网络时,子网管理器自动给每个端口分配了LID。我心想这多省事啊。结果后来要手动指定路径时,才发现LID是动态的,重启后可能变。所以啊,别在代码里硬编码LID。

GID:RoCEv2的全球地址

GID是128位的,基于IPv6格式。RoCEv2用GID来标识端口,同时也在IP层用这个地址做路由。GID通常由网卡的MAC地址和子网前缀组合而成。

GID的格式是这样的:

GID = 子网前缀(64位) + 接口ID(64位)
子网前缀:通常是fe80::/10(链路本地)或全局唯一前缀
接口ID:基于MAC地址(EUI-64格式)

对比总结:

特性 LID(IB) GID(RoCEv2)
长度 16位 128位
作用域 子网内 全局
分配方式 子网管理器自动分配 基于MAC和前缀生成
路由基础 线性转发表 IP路由

QP与CQ:RDMA通信的核心抽象

QP(Queue Pair)和CQ(Completion Queue)是RDMA编程中最基本的概念。你想想看,没有这两个东西,RNIC根本不知道要干什么。

QP:收发一对

QP由两个队列组成:发送队列(SQ)和接收队列(RQ)。应用程序把Work Request(WR)扔进SQ或RQ,RNIC就按顺序处理。QP有三种类型:

  • RC(可靠连接):一对一,可靠传输,支持原子操作。我最常用的类型。
  • UC(不可靠连接):一对一,但不可靠。偶尔用在性能极致场景。
  • UD(不可靠数据报):多对多,类似UDP。适合广播场景。

我在项目中遇到过一个问题:RC类型的QP,如果对端挂了,本端要等很长时间才能检测到。后来我用了共享接收队列(SRQ),才解决了连接数过多时的资源浪费问题。

CQ:完成通知

CQ用来存放完成事件(Completion Event)。当RNIC处理完一个WR后,会在CQ中生成一个CQE(Completion Queue Entry)。应用程序通过轮询或事件通知来获取CQE。

CQ的设计有个讲究:多个QP可以共享一个CQ,也可以一个QP独占一个CQ。我个人的习惯是,延迟敏感的应用用独占CQ,吞吐量优先的应用用共享CQ。

调试技巧:我曾经在调试一个RDMA程序时,发现CQ里总是出现错误CQE。查了半天,原来是接收端没提前post接收WR。记住:RC模式下,接收端必须先post好接收WR,发送端才能发数据。这个顺序搞反了,就会报错。

小结

好了,这一章的内容就这些。我们讲了IB和RoCEv2的架构差异,RNIC的工作原理,GID和LID的寻址机制,还有QP和CQ的概念。这些东西是RDMA的基石,后面所有的高级特性都建立在它们之上。

下一章,我们会深入QP的状态机和连接管理。到时候我会分享一个我在生产环境中遇到的QP状态异常案例,保证让你印象深刻。

课后思考:为什么RoCEv2需要PFC支持,而IB不需要?想明白这个问题,你就理解了两种架构在设计哲学上的根本差异。

公众号:蓝海资料掘金营,微信deep3321