GPU Direct相关技术和原理

10阅读 0评论2024-12-22 lvyilong316
分类:LINUX

GPU Direct相关技术和原理

——lvyilong316

NVIDIA20106月开始推广并使用GPU Direct技术,并持续演进。NVIDIA GPUDirect 是一系列技术集合,旨在优化 GPU 之间 (P2P) GPU 与第三方设备 (RDMA) 之间的数据传输。GPU Direct 技术包括 GPUDirect StorageGPUDirect RDMAGPUDirect P2P GPUDirect Video等。

GPUDirect Shared Memory

2010年,GPUDirect Shared Memory,支持GPU与第三方PCI Express设备(网卡、存储等)通过共享pin住的host memory实现共享内存访问从而加速通信。

从上图对比可以看到,在没有使用GPUDirect情况下,GPU需要将数据从显存复制到GPU驱动在系统内存中pin住的Memory 1,再从Memory 1复制到Memory 2,之后才能再做进一步传输。

使用了GPUDirect Shared Memory之后,内存空间实现了共享,程序pin住一段内存后,既可以用作与GPU的数据交互,减少了一次数据复制,降低了数据交换延迟。

GPUDirect P2PPeer-to-Peer

2011年,第二代GPUDirect技术被称作GPUDirect P2P,重点解决的是节点内GPU通信问题。两个GPU可以通过PCIe P2P直接进行数据搬移,避免了主机内存和CPU的参与。

如图所示,在同一PCI Express总线上,P2P支持GPU之间直接互访显存,不需要将数据复制到系统内存进行中转,进一步降低了数据交换的延迟。

GPUDirect RDMA(GDR)

2013年,在硬件RDMA支持的基础上,nvidia发布GPUDirect RDMA,增加了RDMA直接访问GPU显存的支持,使得第三方PCI Express设备(网卡、存储等)可以bypass CPU host memory直接访问GPU,完美的解决了服务器之间GPU卡通信问题。

接下来我们以 GPUDirect RDMA为例,探讨上述技术究竟如何落实。在上述GPUDirect RDMA的示意图中,我们只看到了GPU和网卡进行数据搬移,但是还有很多问题其实都被忽略掉了。那么,如果我们将上述通信过程细化,至少有三个关键问题需要解决。首先,为了实现CPU控制通信,数据进行网卡和GPU之间P2P搬移数据,我们要解决网卡直接读写GPU显存的问题;其次,为了实现GPU直接控制通信,还有两个问题要解决。其一,GPU如何访问通信资源?其二,GPU如何与网卡进行同步?

首先,对于网卡读写GPU显存,我们不妨先回顾一下网卡是如何访问CPU内存的。对于用户进程而言,其将一个虚拟地址传递给,通过注册内存区域获取物理页表项,然后将页表填入网卡的MTT表中。在这个过程中,内存中建立了页表项,同时,pin memory的操作对每个物理内存页的元数据进行了修改,对于网卡而言,其动作是进行虚拟地址到物理地址的转换,然后发起PCIe请求,至于物理地址映射到主机内存还是设备内存它并不关心。因此,如果我们能够解决向网卡注册GPU虚拟地址的问题,就等价于解决了网卡读写GPU显存的问题。

但是,当我们直接使用一个GPU虚拟地址进行内存注册时,会得到一个Segmetation Fault的错误。因为reg_mr注册时会陷入内核,通过调用get_user_pages获取物理页表,但对于GPU虚拟地址,CPU并不存在其对应的页表项,自然会出现错误。为了实现GPU内存的注册,需要对驱动进行一定的修改。

为了实现网卡对其它设备内存的注册,MLNX提供了一套标准的注册框架。所有的设备驱动需要向MLNX的设备管理模块进行注册,提供类似于操作系统get_ser_pages当网卡驱动需要对一个地址进行注册时,会对地址进行判断,然后调用相应的函数获得设备指针,{BANNED}最佳终调用设备驱动中的物理页获取函数,得到设备内存的物理地址

这张图是Nvidia提供的MLNX驱动框架之间的对接,具体细节就不再详细阐述了。

在新的注册框架下,通过内存注册,GPU内存在BAR空间的地址被下发到网卡,当网卡使用这些地址读写GPU显存时,GPU内部的HSHUB再进行一次,将BAR空间地址映射为实际的显存页面。这里有一个隐含的TrickGPU的虚拟地址到物理地址也是分页寻址的。

至此,网卡能够直接读写GPU显存了,这也就意味着我们已经实现了CPU控制的GPU通信,同时数据通过PCIe P2P进行传输。接下来我们重点考虑GPU直接控制的通信方式。

GPU控制通信其实就是GPU对通信资源进行相应的操作。为了解决这个问题,我们首先从一个比较宏观的角度来看CPU进行RDMA通信时包含哪些操作。从下图中的分类可以看到,CPU在通信过程中,除了提交工作请求和同步,其它所有工作都在进行通信资源的创建时设置,而且都需要和内核进行交互。首先,GPU没有必要管理这些资源创建过程,其次,GPU上的代码也没有办法直接跟主机操作系统进行交互。因此,唯一能够且有必要由GPU控制的流程就是提交工作请求和。接下来我们重点考虑这一过程的实现。

在提交工作请求中,涉及到的通信资源有哪些呢?按照资源类型,可以分为上下文资源,队列资源和。按照资源所处的位置,又可以分为位于设备内存和主机内存。其中,部分资源是仅由网卡进行访问的,这部分资源可以留在主机内存中保持不变,例如队列基地址,通信序列号等。另一部分资源是由CPU进行读写或临时创建的,因此需要详细考虑。

当我们迁移到GPU的场景下,我们发现,GPU要访问这些通信资源,至少要考虑两个问题。

首先,doorbell资源位于网卡,GPU要控制通信必然要涉及到写doorbell,也就是GPU如何访问网卡寄存器的问题。第二个问题在于,除了刚才提到的网卡要直接操作的资源,GPU控制的这些资源,可以放在,也可以放在显存中。也就是这些通信资源放置的位置问题。

对于问题一,我们还是先参考CPU是如何访问网卡BAR空间的。在CPU的页表中其实包含两种表项,一种表项指向实际的物理内存,表项的创建发生于出现缺页异常,另一种表项指向IO设备空间,由ioremap函数创建,或者直接通过MMIO将设备内存被映射到CPU的虚拟地址空间。

对于GPU而言,使用cudaHostMemRegistercudaHostGetDevicePointer等函数可以建立GPU虚拟地址到CPU内存空间的映射,也就是在GPU中建立到主机内存的页表项。

但在早期的CUDA版本中,如果使用ioremap映射后的地址进行注册,会引发段错误。在CUDA 4.0之后该问题被修正,PCIe BAR空间能够直接映射到GPUGPU访问门doorbell存器的问题得以解决。

下面看通信资源放置的位置问题,放在显存中显然可以加速对通信资源的访问,但如果通信连接较大,势必会消耗大量的显存资源,因此需要进行折衷考虑。所以一般GDR按照如下方式进行内存分工:

 

{BANNED}最佳后一个问题就是GPU如何提交网络请求并与网卡进行同步。实际上有ibv_post_sendibv_post_recvibv_poll_cq三个函数就可以了,因此,需要做的事情就是将libibverbs移植到GPU上执行。移植本身没有什么太大的难度,大部分libibverbs库的代码都可以直接在GPU上运行。至此,我们解决了全部GPU控制通信的问题。

我们对整个过程做一个小结和回顾。首先,我们修改了MellanoxNvidia的内存注册部分,达到了网卡直接读写GPU显存的目的,实现了CPU控制的GPUDirect;接着,我们对通信资源进行划分,结合内存映射部分的修改,达到了GPU访问通信资源的目的,{BANNED}最佳后对libibverbs进行代码移植,{BANNED}最佳终实现了GPU控制的GPUDirect功能。

GPUDirect Storage(GDS

就像GPUDirect RDMA(远程直接内存访问)在网络接口卡(NIC)和GPU内存之间直接传输数据时改善了带宽和延迟一样,一种名为GPUDirect Storage的新技术使本地或远程存储(如NVMeNVMe over FabricNVMe-oF))与GPU内存之间建立了直接数据路径。 无论是GPUDirect RDMA还是GPUDirect Storage,都通过CPU内存中的反弹(Bounce)缓冲区(Buffer)避免了额外的数据复制,并且通过在靠近NIC或存储设备的直接内存访问(DMA)引擎上移动数据,实现了直接路径进出GPU内存,而无需增加CPUGPU的负担。对于GPUDirect Storage来说,存储位置并不重要;它可以位于机箱内部、机架内部或通过网络连接GPUDirect Storage已经合并进了CUDA 11.4以后的版本,不需要再单独的部署安装。

传统的场景下,RDMAIO都是从存储和Host RAM之间进行交互,如果GPU需要访问数据依然需要通过CPU RAM复制到GPU RAMGDS使用与GPUDirect RDMA相同的技术,允许远程存储系统直接在GPU上寻址内存,并消除了CPU RAM缓冲区以及复制的问题。通过将新的内核驱动程序(nvidia-fs.konvidia.ko)插入VFS堆栈来完成,该堆栈管理GPU内存地址空间并将IO定向到CPU RAMGPU RAM的指定块。数据通过PCI总线直接在GPU和网络接口之间移动。元数据等许多操作仍将使用CPU RAM,而数据块可以直接进入GPU RAMnvidia-fs.ko负责把GPU的内存(GPU的部分BAR空间)给到文件系统。整个软件栈如下:

更具体一点:

用户态的代码写的时候,就变成下面这样了:

其中cudaMalloc/cuFileBufRegister会从GPU内存分配,并调用nvidia-fs.ko做映射,得到一个vagpu pa/dmacpu pa的映射,后面再调用cuFileRead/cuFileWrite的时候把这个va传递给虚拟文件系统VFS,并通过kernelcall_write_iter/call_read_iter函数进行文件读写,之后底层sas控制器或者nvme控制器的驱动通过dma_map相关函数把这个va又转换成具体的pa,把内容读或者写到这块地址中,具体流程如下:

具体可以参考代码:

GPUDirect Async

上面我们讲通过GDS,已经可以让RDMA网卡访问GPU的内存了。但这里面其实还有两种方式,{BANNED}最佳开始GDS并不是GPU直接调用verbs接口控制数据发送和接收的,而仅仅是将内存注册到网卡,收发包的控制依然是CPU,如下图所示。

为了让GPU和网卡并行起来,CPU仍然扮演了厚重的调度角色,而且GPU空转时间比较长。以上逻辑对应代码如下:

CPU之所以区别于GPU的主要原因是:CPU更擅长完成更加复杂的任务,而不是执行计算密集的特定任务。但是NVIDIA作为GPU的领导者,它是准备改变这个现状。通过一篇论文《GPU-Initiated On-Demand High-Throughput Storage Access in the BaM System Architecture》解释了NVIDIA设计提出了一种称为BaMBig accelerator Memory)的新系统架构。BaM 的设计目标是为 GPU 线程提供高效的抽象,以便轻松地按需、细粒度地访问存储中的海量数据集,并实现比{BANNED}最佳先进的解决方案更高的应用程序性能。

目前缺乏在不依赖 CPU 的情况下编排存储访问的 GPU 机制,为了解决这个问题,BaM 提供了一个用户级 GPU 库,其中包含 GPU 内存中的高度并发提交/完成队列,使按需访问未命中软件缓存的 GPU 线程能够以高吞吐量方式进行存储访问。为此,BaM GPU 内存中配置了存储 I/O 队列和缓冲区,如下图所示,基于 GPU {BANNED}最佳近的内存映射功能将存储DB reg映射到 GPU 地址空间

通过对比原有的存储数据访问模式(GDS),我们明显的可以看到BaM 使 GPU 线程能够直接访问存储,从而实现细粒度的计算和 I/O 重叠。首先,降低了 CPU-GPU 同步和 GPU 内核启动的频率,不用每次数据访问都需要CPU来启动和调度。其次,CPU调度的场景以大块数据任务为单位,而不是GPU真实需要的随机数据,因此带来了很大的IO放大,使用新的模式,避免了IO放大。{BANNED}最佳后使用新的方式,业务设计不需要考虑数据集大小的问题,以前需要通过应用层面的数据分块和分割来处理数据,现在就可以一套方案应对不同的数据规模。 

结合BAM的技术研究,在AI的图景下,英伟达推出的是NVIDIA DOCA GPUNetIODOCA GPUNetIO库支持NICGPU之间通过一个或多个CUDA内核进行直接通信。这会从关键路径中删除 CPU。,并且将这部分IO的调度工作集成到了CUDA框架中。GPUDirect Async Kernel-Initiated NetworkGPUCUDA 内核)可以直接与网卡交互,在 GPU 内存 (GPUDirect RDMA) 中发送或接收数据包,而无需 CPU 的干预。具体流程如下:

对应代码流程如下所示

借用一张图表示GPUDirect技术全景,如下所示:

与代理发起的通信相比,IBGDA使用GPUDirect Async-Kernel-InitiatedGPUDirect Async-KI)使GPU SM能够直接与NIC进行交互。如上图2所示,这涉及以下步骤:应用程序启动一个CUDA核函数,在GPU内存中生成数据。应用程序调用NVSHMEM操作(如nvshmem_put)与另一个PE进行通信。NVSHMEM操作使用SM创建NIC工作描述符,并直接将其写入WQ缓冲区。与CPU代理方法不同,这个WQ缓冲区位于GPU内存中。SM更新位于GPU内存中的DBR缓冲区。SM通过向NICDB寄存器写入来通知NICNIC使用GPUDirect RDMA读取WQ缓冲区中的工作描述符。NIC使用GPUDirect RDMA读取GPU内存中的数据。NIC将数据传输到远程节点。NIC通过使用GPUDirect RDMACQ缓冲区写入来通知GPU网络操作已完成。正如所示,IBGDA从通信控制路径中排除了CPU。在使用IBGDA时,GPUNIC直接交换通信所需的信息。为了通过SM访问时提高效率,WQDBR缓冲区也被移到GPU内存中,同时保留NIC通过GPUDirect RDMA的访问能力。

当然,以上只是GPUDirect Async(GDA)GDS场景的应用流程,同样GDA也可以用在GDR等场景。将GPUDirect Async引入NVSHMEM中的实现成为InfiniBand GPUDirect AsyncIBGDA)的通信方法,该方法建立在GPUDirect Async系列技术之上。它使GPU能够在发出节点间NVSHMEM通信时绕过CPU,而无需对现有应用程序进行任何更改。这为使用NVSHMEM的应用程序带来了吞吐量和扩展的显着改进。

Nvidia Magnum IO

有了上面这些网络加速、IO加速技术之后,Nvidia更进一步提出了Magnum IO技术,把这些都打包在了一起。不得不说Nvidia在造概念和名词上真是老手。

上一篇:NCCL源码解析2——bootstrap网络初始化
下一篇:没有了