RDMA 被用在很多地方,主要是因为它可以实现高性能。在这篇文章中,我将提供有关如何从多个方面优化 RDMA 代码的建议和技巧
简单的科普下RDMA
什么是RDMA?
DMA 代表直接内存访问。这意味着应用程序可以在 CPU 干预的情况下直接访问(读/写)主机内存。如果您在主机之间执行此操作,它将成为远程直接内存访问 (RDMA)
在阅读有关 RDMA 的内容时,您会注意到一些用于描述其优点的术语。 “零复制 Zero Copy”、“内核绕过 Kernel Bypass”、“协议卸载 Protocol Offload”和“协议加速 Protocol Acceleration”等术语
RDMA 的工作原理
RDMA 的工作原理是通过硬件路径(NIC 和网络)将数据从一台主机上的用户应用程序内存直接复制到另一台主机上的用户应用程序内存中。RDMA是下图中的蓝线(图 3)。绿线描绘了您早已了解的传统 TCP/IP 流量
请注意,如果应用程序驻留在内核空间中,它“仅”绕过操作系统堆栈和系统驱动程序,但这仍然提供了显著的性能提升
零复制和内核绕过
这些术语指的是 RDMA 提供的速度增益,因为与正常的 TCP/IP 行为相比,数据不必多次从应用程序内存缓冲区复制到内核内存缓冲区。RDMA通过其他复制操作传递到 NIC,NIC 通过网络将其发送到其他主机。在另一台主机上,必须发生相反的过程。 NIC 内存缓冲区中的数据被复制到内核空间,并在内核空间中再次复制并发送到用户空间到应用程序内存缓冲区。
您会注意到正在进行大量复制,这是 RDMA 避免的开销(零复制)。通过这样做,还可以避免用户空间和内核空间之间的上下文切换(内核绕过)。这极大地加快了速度。
CPU 卸载/旁路
该工作实际上被卸载到 NIC,因此它绕过了主机的 CPU(CPU Offload 或 Bypass)。这样做有两个优点:
1. 应用程序可以访问(远程)内存,而无需内核和用户空间进程消耗任何主机 CPU 周期进行读写。
2. CPU 中的缓存不会被访问的内存内容填充。
CPU 周期和缓存可用于实际的应用程序工作负载,而不是用于移动数据。其好处通常是 CPU 开销减少 20% 到 25%
传输协议加速
最后,RDMA 可以执行称为协议加速的操作。基于消息的事务以及进行收集/分散(SGL)的能力(读取多个缓冲区并将它们作为一个整体发送,同时将它们写入多个接收缓冲区)加快了实际数据移动的速度。这有很多细节。 SNIA(存储网络行业协会)和学术研究人员的演示文稿和出版物很容易在互联网上获得,并且可能变得相当技术性
结论是 RDMA 协议本质上是很快的。
为什么我们需要它?
除了高性能计算之外,过去十年中我们还看到各种东西方流量持续大幅增长。这始于虚拟化,它与虚拟机移动性一起带来了性能和性能的提升。可扩展性对许多资源(网络、存储、计算)提出了挑战。最重要的是,我们看到超融合基础设施(HCI)、存储复制和其他用例等趋势消耗了更多的带宽。与此同时,随着我们获得更快的存储选项(NVMe、各种类型的 NVDIMM(N、F、P)或英特尔的 3D XPoint),对超低延迟的需求也随之增加,这导致了围绕其功能的新架构。
图 4:毫无疑问,非易失性 DIMM(及其所有变体)将进一步推动对 RDMA 的需求(图片由 DELL 提供)
一般建议
1. 避免在数据路径中使用控制操作
与保留在调用它们的同一上下文中(即不执行上下文切换)并且以优化方式编写的数据操作不同,控制操作(所有创建/销毁/查询/修改)操作非常昂贵, 因为:
-
大多数时候,他们执行上下文切换
-
有时他们分配或释放动态内存
-
有时他们参与访问 RDMA 设备
作为一般经验法则,应避免调用控制操作或减少其在数据路径中的使用。
以下动词(verbs接口)被视为数据操作:
-
ibv_post_send() 发送
-
ibv_post_recv() 接收
-
ibv_post_srq_recv() 共享接收队列
-
ibv_poll_cq() 轮询完成队列
-
ibv_req_notify_cq 通知完成
2. 发布多条 WR 时,一次调用将它们发布到列表中
当用verbs接口ibv_post_*()中的一个, 来发送工作请求WR时,在一次调用中将多个工作请求作为链表发布,而不是每次使用一个工作请求进行多次调用,将提供更好的性能,因为它允许低级驱动程序执行优化。
3. 使用工作完成事件时,在一次调用中确认多个事件
使用事件处理工作完成时,在一个调用中确认多个完成而不是每次调用多个调用将提供更好的性能,因为执行的互斥锁较少。
4. 避免使用许多分散/聚集条目
在工作请求(发送请求或接收请求)中使用多个分散/聚集条目意味着 RDMA 设备将读取这些条目并将读取它们引用的内存。使用一个分散/聚集条目比使用多个分散/聚集条目提供更好的性能。
5. 避免使用围栏(Fence)
设置了栅栏标志的发送请求将被阻止,直到所有先前的 RDMA 读取和原子发送请求完成。这会降低带宽。
6. 避免使用原子操作
原子操作允许以原子方式执行读取-修改-写入。这通常会降低性能,因为这样做通常涉及锁定对内存的访问(取决于实现)。
7. 一次读取多个工作完成情况
ibv_poll_cq()允许一次读取多个完成。如果CQ中的工作完成数小于尝试读取的工作完成数,则意味着CQ为空,无需检查其中是否还有更多工作完成。
8. 设置特定任务或进程的处理器关联性
当使用对称多处理 (SMP) 机器时,将进程绑定到特定的 CPU/核心可以更好地利用 CPU/核心,从而提供更好的性能。按照机器中 CPU/核心的数量执行进程并将进程分布到每个 CPU/核心可能是一个很好的做法。这可以通过“taskset”实用程序来完成。
9. 使用本地 NUMA 节点
在非统一内存访问 (NUMA) 计算机上工作时,将进程绑定到被视为 RDMA 设备的本地 NUMA 节点的 CPU/核心可能会因为更快的 CPU 访问而提供更好的性能。将进程分布到所有本地 CPU/核心可能是一个很好的做法。
10. 使用缓存行对齐的缓冲区
与使用未对齐的内存缓冲区相比,使用缓存行对齐的缓冲区(在 S/G 列表、发送请求、接收请求和数据中)将提高性能;它将减少 CPU 周期数和内存访问次数。
11. 避免进入重传流
重传是性能杀手。RDMA中重传的主要原因有2个:
-
传输重传 - 远程 QP 未处于可以处理传入消息的状态,即至少未达到 RTR 状态,或移至错误状态
-
RNR重传-响应方有一条消息应该消耗一个接收请求,但接收队列中没有任何接收请求
有些 RDMA 设备提供计数器来指示重试流发生,但并非全部。
当QP进入这些流时,将QP.retry_cnt和QP.rnr_retry设置为零将导致失败(即,带有错误的完成)。
但是,如果无法避免重试流,请在重传之间使用较低(尽可能)的延迟。
提高带宽的手段
1. 找到最适合 RDMA 设备的 MTU
MTU 值指定可以发送的最大数据包有效负载大小(即不包括数据包标头)。根据经验,由于所有 MTU 值的数据包标头大小都相同,因此使用最大可用 MTU 大小将降低每个数据包的“支付价格(负载开销)”;有效负载数据占总使用带宽的百分比将会增加。但是,有些 RDMA 设备可以为低于最大支持值的 MTU 值提供最佳性能。人们应该执行一些测试,以便为他使用的特定设备找到最佳的 MTU。
2. 使用大消息
发送几条大消息比发送大量小消息更有效。在应用程序级别,第一级应该通过 RDMA 收集数据并发送大消息。
3. 处理多个未完成的发送请求
处理多个未完成的发送请求并保持发送队列始终满(即,对于每个轮询的工作完成发布一个新的发送请求)将使 RDMA 设备保持忙碌并防止其闲置。
4. 配置队列对以允许并行进行多个 RDMA 读取和原子操作
如果使用 RDMA 读取或原子操作,建议将 QP 配置为与运行中的多个 RDMA 读取和原子操作配合使用,因为它将提供更高的 BW。
5. 使用发送队列中的选择性信号
在发送队列中使用选择性信号意味着并非每个发送请求在结束时都会产生工作完成,这将减少应处理的工作完成的数量。
降低延迟的手段
1. 使用轮询(polling)读取工作完成情况
为了在工作完成添加到完成队列后立即读取它们,轮询将提供最佳结果(而不是使用工作完成事件)。
2. 以内嵌方式发送小消息
在支持内联发送数据的 RDMA 设备中,内联发送小消息将提供更好的延迟,因为它消除了 RDMA 设备(通过 PCIe 总线)执行额外读取以读取消息有效负载的需要。
3. 在 QP 的超时和 min_rnr_timer 中使用较低的值
在 QP 的超时和 min_rnr_timer 中使用较低的值意味着,如果出现错误并且需要重试(无论是因为远程 QP 没有应答还是没有未完成的接收请求),重传之前的等待时间将简短一点。
4. 如果使用立即数据,请使用 RDMA Write with立即而不是 Send with立即
当发送仅包含立即数据的消息时,带有立即数的 RDMA 写入将比带有立即的发送提供更好的性能,因为后者会导致未完成的已发布接收请求被读取(在响应方),而不仅仅是被消耗。
减少内存消耗的手段
1. 使用共享接收队列 (SRQ)
使用 SRQ 可以节省未完成的接收请求的总数,从而减少消耗的总内存,而不是为每个队列对发布许多接收请求。
2. 注册物理连续内存
寄存器物理连续内存(例如大页)可以允许低级驱动程序执行优化,因为需要较少量的内存地址转换(与 4KB 内存页缓冲区相比)。
3. 将使用的队列大小减少到最小
创建各种队列(队列对、共享接收队列、完成队列)可能会消耗大量内存。人们应该将它们的大小设置为其应用程序所需的最小值。
减少CPU消耗的手段
1. 处理工作完成事件
使用事件读取工作完成将消除在 CQ 上执行持续轮询的需要,因为当工作完成添加到 CQ 时,RDMA 设备将发送事件。
2. 在响应者端处理请求的事件
当在响应者端读取工作完成时,请求事件可以是向请求者提供提示的好方法,表明现在是读取完成的好时机。这减少了已处理的工作完成总数。
3. 与多个队列共享同一个CQ
对多个队列使用相同的 CQ 并减少 CQ 的总数将消除检查多个 CQ 的需要,以便了解未完成的工作请求是否已完成。这可以通过与多个发送队列、多个接收队列或它们的混合共享相同的 CQ 来完成。
提高可扩展性的手段
1. 使用集体算法(collective algorithms), 可参考DAOS中CART的k项树算法
使用集体算法将减少通过线路的消息总数,并减少集群中每个节点将使用的消息和资源总数。有些 RDMA 设备提供特殊的集体卸载操作,有助于降低 CPU 利用率。
2. 使用不可靠数据报 (UD) QP
如果每个节点都需要能够接收消息或向子网中的任何其他节点发送消息,则使用连接的 QP(可靠或不可靠)可能是一个糟糕的解决方案,因为将在每个节点中创建许多 QP。使用 UD QP 更好,因为它可以从子网中的任何其他 UD QP 发送和接收消息。
参考
Nvidia_Mellanox_CX5和6DX系列网卡_RDMA_RoCE_无损和有损_DCQCN拥塞控制_动态连接等详解-一文入门RDMA和RoCE有损无损: Nvidia_Mellanox_CX5和6DX系列网卡_RDMA_RoCE_无损和有损_DCQCN拥塞控制_动态连接等详解-一文入门RDMA和RoCE有损无损-腾讯云开发者社区-腾讯云
-
分享 撰文: Dotan Barak ,2013年6月8日。2019年3月9日。
Tips and tricks to optimize your RDMA code - RDMAmojo RDMAmojo
晓兵(ssbandjl)
博客: https://cloud.tencent.com/developer/user/5060293/articles | https://logread.cn | https://blog.csdn.net/ssbandjl
欢迎对高性能分布式存储PureFlash, SPDK, RDMA, 等高性能技术感兴趣的朋友加入PureFlash技术交流(群)
晓兵技术杂谈(系列)
https://cloud.tencent.com/developer/user/5060293/video
欢迎对DAOS, SPDK, RDMA等高性能技术感兴趣的朋友加我WX(ssbandjl)进入DAOS技术交流(群)文章来源:https://www.toymoban.com/news/detail-779033.html
DAOS汇总: https://cloud.tencent.com/developer/article/2344030文章来源地址https://www.toymoban.com/news/detail-779033.html
到了这里,关于优化 RDMA 代码的建议和技巧-rdma性能优化技巧-避坑指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!