GPU应用程序越来越需要网络和数据移动表现为GPU直接控制的操作,而不是依赖主机驱动的服务。当CPU处于每次网络事务的中间位置时,它会成为关键路径上的瓶颈,增加延迟并限制分布式应用程序实时响应的效率。

NVIDIA DOCA GPUNetIO是一个面向实时数据包处理和数据移动的GPU中心化网络SDK层,直接解决了这一问题。它整合了GPUDirect RDMA、GPUDirect异步内核发起(GDA-KI)和GDRCopy等技术,使CUDA内核能够直接驱动以太网、RDMA、Verbs和DMA操作,同时让CPU远离应用程序的关键路径。

具体来说,DOCA GPUNetIO提供以下两方面功能:

控制路径上的CPU函数,用于将使用DOCA Ethernet、DOCA Verbs、DOCA DMA、DOCA CommChannel创建的传输对象导出到GPU内存

数据路径上的GPU CUDA函数,允许创建能够操控已导出传输对象的CUDA内核

自NVIDIA首次推出以GPUNetIO为基础的GPU中心化数据包处理技术以来,该框架已经显著成熟,从一种将CPU移出关键路径的强大方式,演变为跨NVSHMEM、NCCL、Aerial 5G SDK、UCX/NIXL、集成Holoscan Sensor Bridge操作符的NVQLink、Holoscan高级网络操作符、DeepEP/HybridEP等的统一GDA-KI基础架构。

本文将介绍最新进展:开源库、统一架构,以及这些集成在实践中的运作方式。

为什么统一的GPUNetIO框架至关重要

在GPUNetIO成为共享基础之前,每个通信库都各自构建了自己独立的GDA-KI风格、GPU发起RDMA的实现方案。每个方案都独立运作,拥有各自的假设、代码和维护负担,彼此之间没有任何共享。

将不同的GDA-KI风格实现统一到GPUNetIO框架下带来了巨大优势:各个库不再需要各自构建和维护基于GDA-KI的RDMA通信路径,而是可以统一汇聚到单一的GPUNetIO GDA-KI实现,供所有人使用、改进和扩展。

这创造了一个共享的投资点,用于功能开发、性能优化和错误修复,减少了各SDK之间的重复工程工作,并使得为某个框架开发的创新能够更快地应用到其他框架中。

换言之,GPUNetIO成为多个通信库可以构建其上的共同GDA-KI基础,而不是一堆平行且部分重叠的实现方案。这种统一方法对于NIXL/UCX、NCCL和NVSHMEM等更高层级的库尤为有价值。它们正在协作丰富GPUNetIO,并共同从整个技术栈的改进中获益。从生态系统角度看,这意味着更少的重复代码、更少的行为碎片化,以及更强的长期演进路径。

GPUNetIO:开源版与SDK版的对比

NVIDIA以两种紧密关联的形式发布GPUNetIO,因为生态系统既需要广度也需要开放性。完整的DOCA SDK版本是超集:这是DOCA编程指南中记录的GPUNetIO实现,涵盖了更广泛的DOCA技术栈,包括(但不限于)Verbs、Ethernet、DMA和Comm Channel集成,同时保留了将控制路径移近GPU、把CPU移出应用关键路径的核心GPUNetIO模型。

与此同时,NVIDIA还发布了一个开源的GPUNetIO项目,作为更轻量级、专注于RDMA-Verbs的实现方案,专为那些希望在网络传输集成方式上保持完全开放的框架而设计。目前,开源版本是较小规模的面向Verbs的子集,而DOCA SDK版本则是添加了更丰富的RDMA支持、Ethernet和DMA等更广泛能力的超集。

这种划分并非要创建两套分化的软件栈,而是为GPU发起的RDMA通信提供一个通用的开源基础,同时在DOCA SDK存在时留有通往更高级功能的路径。实际上,开源实现可以在运行时检测DOCA SDK的存在,如果可用,就通过dlopen调用部分闭源的DOCA SDK函数;如果SDK不存在,则继续使用开源实现运作。

这正是关键的架构优势所在:每个通信库不再需要构建和维护自己私有版本的GDAKI风格RDMA底层机制,GPUNetIO成为多个框架和库可以共同采用、加固和改进的共享实现点。

在CUDA设备端,针对Verbs路径,开源版本和SDK版本之间的差异被有意设计得很小:两个版本都暴露了面向设备的API,这使得即使主机端实现从轻量级开源路径扩展到更广泛的DOCA SDK功能集,GPU编程模型在很大程度上保持一致。

编程模型

本节介绍适用于任何GPUNetIO应用程序的关键概念和编程模型要素。

CPU控制路径

在使用DOCA GPUNetIO的应用程序中,需要先进行主机端CPU配置阶段来执行控制路径,随后进行第二阶段,即数据路径通过CUDA内核在GPU上运行。

控制路径的工作流程通常遵循以下顺序:

初始化和配置GPU及网络设备,并分配所需的内存。

创建网络传输对象。例如,DOCA Verbs或DOCA Ethernet通过CPU上的mlx5dv创建网络队列对象。

GPUNetIO CPU函数将网络队列对象的相关元素导出到存储在GPU内存中的描述符,并为其提供一个GPU地址。

应用程序启动CUDA内核,将网络队列的GPU描述符作为其中一个输入参数传递进去。

为简化DOCA Verbs的某些操作,GPUNetIO引入了一组高级函数(例如doca_gpu_verbs_create_qp_hl),将创建和连接RDMA QP所需的步骤进行了压缩整合。这些函数在SDK版本和开源版本中均有提供。

控制路径完成后,数据路径即可在GPU上启动,此时一个或多个CUDA内核使用GPUNetIO CUDA函数对导出到GPU内存的传输对象进行操作,以发送或接收流量。

GPU数据路径

用于GPU通信的CUDA内核的一般结构通常包括以下步骤:

发布WQE:一个或多个CUDA线程向网络队列对象发布工作队列条目(WQE),例如RDMA写、RDMA读或Ethernet发送/接收。

敲响门铃:一个或多个CUDA线程通过写入网卡寄存器来通知网卡新的WQE已准备好(即"敲门铃")。

轮询CQE(可选):一个或多个CUDA线程等待完成队列条目(CQE),以确认WQE已成功完成。

API:高级与低级

针对Ethernet和RDMA Verbs传输,DOCA GPUNetIO提供两种不同层级的API:高级和低级。

高级API提供复杂和复合操作的实现。例如,在Verbs方面,doca_gpu_dev_verbs_put_signal()提供了一种预实现的线程安全组合方式,将RDMA写工作队列条目(WQE)与RDMA原子加操作(Fetch & Add WQE)相结合,可以在每线程范围或每warp范围内执行(warp中的所有线程协同操作,发布WARP_SIZE个RDMA写操作,最后只进行一次RDMA原子操作)。该函数负责处理同一网络队列上其他CUDA线程并发提交WQE的情况,并敲响网卡门铃以避免竞态条件。

类似地,在Ethernet方面,另一个例子是doca_gpu_dev_eth_txq_send(),它提供了一种线程安全的组合方式,在网络队列上发布Ethernet发送WQE,然后在线程、warp或块级别敲响门铃。接收端也采用同样的方式。

相比之下,低级API提供了基本的构建模块,供应用程序创建自定义的复合操作,例如发布不同操作码的WQE、敲响网卡门铃、轮询完成队列以等待完成队列条目(CQE)。这些API不具备线程安全性,因此应用程序需要负责对同一网络队列对象的并发访问(如果存在)进行适当的同步处理。

敲响门铃

一旦WQE已发布到网络队列,就必须通知网卡以便其执行这些操作。这一步骤称为"敲响门铃"。在GPUNetIO中,这指的是CUDA内核通知网卡有新的工作准备执行。

DOCA GPUNetIO提供了几种敲响门铃的方式:

常规门铃:这是标准模式,网卡寄存器通过MMIO映射到CUDA内存空间,允许CUDA线程直接写入。

BlueFlame:这遵循与常规门铃相同的总体模型,但在这种情况下,整个WQE(而不仅仅是通知)会被写入网卡寄存器。BlueFlame通常用于网络队列数量较少、对延迟敏感的应用程序。

CPU辅助门铃:在这种模式下,网卡寄存器映射到CPU内存而非GPU内存。GPU将通知写入一个由CPU线程轮询的共享内存区域。一旦CPU线程检测到GPU的更新,就会敲响网卡门铃。这种模式主要用于在没有GPU与网卡直连的系统(如DGX Spark)中启用GDA-KI。

示例代码

为了便于API探索和性能基准测试,GPUNetIO提供了各种参考实现。开发人员可以直接在项目仓库中访问开源示例,而更全面的SDK示例和专用的基于Ethernet的应用程序则作为完整DOCA SDK的一部分发布。此外,这些示例在最近的版本中已集成到GitHub中,以便于访问。

以下部分将重点展示具体的代码片段,以说明如何利用高级和低级原语通过不同的编程语义实现类似的逻辑。

Ethernet示例

GPU发起的Ethernet数据包生成器可以使用高级API实现,如下方代码片段所示。具体来说,高级函数doca_gpu_dev_eth_txq_send有助于多个CUDA块向共享的发送队列并发提交WQE,内部处理必要的同步,确保线程安全的操作,而无需定制应用层级的锁机制。此外,它抽象掉了队列结构的细粒度管理,如追踪WQE和CQE的具体索引。

(此处为代码示例)

应用程序还可以利用低级原语来实现对实现细节的精确控制。例如,如果数据包生成器为每个CUDA块精确分配一个发送队列,那么高级函数doca_gpu_dev_eth_txq_send内部的同步开销就变得不必要。在这种情况下,可以使用以下模式简化逻辑:

(此处为代码示例)

Verbs示例

GPUNetIO Verbs API遵循类似的范式。例如,开发人员可以利用高级原语,通过以下put功能实现一个性能导向的应用程序,复现ib_write_bw的逻辑,并通过GPU驱动的通信方式进行:

(此处为代码示例)

应用程序还可以利用低级原语来实现对实现细节的精确控制,例如创建一个针对特定CUDA块的带宽测试,手动管理每个具体操作:

(此处为代码示例)

用于GIN的NCCL设备API

NCCL GIN(GPU发起网络)为CUDA内核提供设备端通信抽象层,暴露put、get、signal、wait和flush等原语。自NCCL 2.27版本起,GIN已将GDA-KI集成为利用开源GPUNetIO Verbs路径的后端。这种架构使NCCL算法和设备API应用程序能够直接从GPU驱动RDMA操作,同时将低级队列管理和工作提交的复杂性抽象到GIN接口之下。

工作流程始于CPU在通信器初始化期间执行控制路径,处理RDMA队列对创建、内存注册以及将传输描述符导出到GPU内存。配置完成后,数据路径完全转移到GPU上。GIN操作被映射为GPUNetIO Verbs调用,管理WQE准备、门铃敲响和完成轮询。这种模型成功地将CPU移出了每次网络事务的应用关键路径。

通过将通信语义与传输实现解耦,NCCL专注于集合算法,而GPUNetIO提供经过强化的GDA-KI底层机制。NCCL利用GIN级别的控制来管理线程、CTA或GPU范围的资源共享,实现请求聚合和优化的队列管理。这些能力与GPUNetIO的优势相契合,促进了低延迟和高吞吐量路径的实现,而不会使RDMA实现产生碎片化。最终,GPUNetIO成为一个共享基础,性能增强和网卡支持只需开发一次,即可在整个GPU通信生态系统中使用。

要探索NCCL GIN接口的功能实现,开发人员可以访问NCCL测试GitHub仓库获取参考代码。

NVSHMEM

NVSHMEM是一种基于PGAS的编程模型,为GPU集群提供可扩展的点对点和集合通信原语,如put、get、原子操作、屏障和归约操作。它使多GPU应用程序能够在CUDA内核中实现细粒度的GPU到GPU数据移动和同步,为应用工作负载的强扩展性带来显著提升。

对于网络通信,NVSHMEM根据可用的网络能力和应用需求提供多种传输方式。NVSHMEM提供了一种IBGDA传输方式,使用MLX5直接Verbs实现GDA-KI。

随着开源GPUNetIO库的发布,NVSHMEM 3.7引入了一种基于GPUNetIO的新传输方式。通过构建在这一通用实现层之上,NVSHMEM能够从GPUNetIO的优化以及通过DOCA SDK提供的高级网卡功能中获益。新的GPUNetIO传输方式同时支持GDA-KI通信和主机发起通信(即CPU通信)通过RDMA实现(在后一种场景中,GPUNetIO仅用于控制路径以配置网络组件,而数据路径则通过NVSHMEM原生函数在CPU上执行)。用户可以通过设置NVSHMEM_REMOTE_TRANSPORT=gpunetio来选择GPUNetIO传输方式,并通过NVSHMEM_GPUNETIO_ENABLE_GDAKI=1启用GDA-KI。

NVSHMEM中GPUNetIO传输方式的GDA-KI部分遵循IBGDA传输方式的结构,但用对GPUNetIO库的调用替换了大部分低级代码。例如,如上文"敲响门铃"部分所述,门铃构造和敲响代码已完全外部化到GPUNetIO库中。在实现与IBGDA相同功能和性能的同时,GPUNetIO传输方式显著降低了传输实现的复杂性,展示了整合GDA-KI实现通用功能的价值。

根据通信模式的不同,启用GDA-KI可能会显著提升小消息大小的可达带宽。以下实验使用NVSHMEM性能测试套件中的shmem_put_bw基准测试,使用nvshmem_double_put_nbi处理不同的消息大小。该实验使用每个CTA(协作线程阵列)1个线程和1个QP,并逐步增加CTA数量。下面两幅图展示了GPUNetIO传输方式所达到的带宽,第一幅图显示了禁用GDA-KI(即使用CPU通信)时的结果,第二幅图显示了使用GDA-KI时的结果。

如上图所示,当NVSHMEM使用CPU通信时(控制CPU路径通过GPUNetIO函数配置,而CPU数据路径为NVSHMEM原生),由于CPU代理瓶颈,在扩展到更多CTA和QP时,小消息大小的带宽受到限制。而在另一幅图中,相同的基准测试使用启用了GDA-KI功能的GPUNetIO后端执行(数据路径由GPU通过GPUNetIO CUDA函数执行)。

实验证实,使用GDA-KI消除了代理瓶颈,带来了更好的CTA和QP扩展行为。GDA-KI版本在较小消息大小下能达到更高的峰值带宽,并且相比非GDA-KI版本,在更小的消息大小时就能达到峰值带宽。此前比较IBRC和IBGDA带宽性能的实验也显示出类似的结果。

总体而言,GPUNetIO保留了IBGDA和IBDEVX的功能和性能,同时大幅降低了NVSHMEM的实现复杂性和维护成本。

NVQLink

NVIDIA NVQLink是NVIDIA用于连接量子处理器控制系统与GPU计算的低延迟参考架构,通过与NVIDIA CUDA-Q的紧密集成,实现量子错误校正(QEC)、前馈和自适应校准等实时量子-经典工作流程。Holoscan Sensor Bridge(HSB)通过使用RoCE和轻量级FPGA IP以及主机端控制软件,为这种方法提供了网络基础,而NVQLink则将基于HSB的模型推向了一个更低延迟的范畴,适用于实时QPU控制,而非传统的传感器数据接入流程。

NVQLink所使用的HSB网络操作符被称为GPU RoCE收发器。它构建于DOCA GPUNetIO和DOCA Verbs库之上,旨在尽可能最小化流量转发延迟。在独立模式下,该操作符提供用于网络健全性检查和性能分析的流量转发执行路径。

该操作符启动一个持久化的CUDA内核,使用DOCA GPUNetIO CUDA函数持续从远程对端接收数据包,并在没有任何CPU干预的情况下将其发送回去。在这种设置中,远程对端是一个FPGA,用于测量数据包从发送到接收返回之间的耗时。

为测量HSB 2.7.0 GPU RoCE收发器操作符的网络转发延迟,我们使用了配备Blackwell GPU和ConnectX-7网卡的NVIDIA IGX Thor平台。

在另一端,通过OSFP电缆背靠背连接,我们使用Xilinx RFSoC 4×2板上的FPGA来发送、接收并测量往返延迟。

测得的最小延迟(即数据包发送到FPGA接收之间的时间)约为2.6微秒,中位延迟为2.7微秒。

开始使用DOCA GPUNetIO

DOCA SDK GPUNetIO编程指南——完整SDK文档

开源GPUNetIO仓库——示例和开源实现

GitHub上的DOCA SDK示例——参考实现

NCCL测试——GIN设备API——GIN参考代码

Holoscan Sensor Bridge GPU RoCE收发器——NVQLink操作符

Q&A

Q1:DOCA GPUNetIO是什么?

A:DOCA GPUNetIO是NVIDIA推出的一个GPU中心化网络SDK层,用于实时数据包处理和数据移动,它整合了GPUDirect RDMA、GDA-KI和GDRCopy等技术,让CUDA内核能够直接驱动网络操作,同时将CPU移出应用程序的关键路径。

Q2:GPUNetIO的开源版本和SDK版本有什么区别?

A:开源版本是一个更轻量级、专注于RDMA-Verbs的实现,适合希望保持完全开放的框架使用;而完整的DOCA SDK版本是超集,涵盖了Verbs、Ethernet、DMA等更广泛的能力。开源版本在运行时可以检测DOCA SDK是否存在,若存在则调用部分SDK功能,否则继续用开源实现运作。

Q3:GPUNetIO与哪些NVIDIA技术和框架进行了集成?

A:GPUNetIO已经作为统一的GDA-KI基础架构,集成到了NVSHMEM、NCCL、Aerial 5G SDK、UCX/NIXL、NVQLink(搭配Holoscan Sensor Bridge操作符)、Holoscan高级网络操作符以及DeepEP/HybridEP等多个NVIDIA技术栈中。

NVIDIA