资讯动态

【cuda] NVSHMEM

发布时间:2026/8/3 18:20:48 来源:尧图企业网站定制
好的这是一个非常核心且关键的概念。我们来深入解释一下“相对于 NVSHMEM 基地址的偏移量完全相同”到底是什么意思。首先忘掉我们平时在单个进程中编程时对指针和内存地址的直觉。在分布式内存环境中尤其是像 NVSHMEM 这样的 PGAS (Partitioned Global Address Space) 模型中内存地址有了新的维度。单进程中的内存地址在普通的 C/CUDA 程序中当你在 GPU 上cudaMalloc一块内存时你会得到一个虚拟地址指针比如0x7f0123450000。这个地址只在当前这个进程的地址空间中有意义。在另一个进程中同样的地址0x7f0123450000可能指向完全不同的物理内存或者根本就是无效地址。NVSHMEM 和 PGAS 模型的魔法NVSHMEM以及其他 PGAS 模型如 OpenSHMEM, UPC引入了一个叫做全局地址空间 (Global Address Space)的概念。它在所有参与的进程在 NVSHMEM 中称为 PE, Processing Elements之上虚拟化出了一个统一的地址空间。这个全局地址空间是分区的 (Partitioned)。这意味着虽然它看起来像一个巨大的、所有进程共享的内存池但实际上它的每一部分都物理地驻留在某一个特定的 PE (GPU) 的显存上。nvshmem_malloc的作用当你调用rdma_buffer_ptr nvshmem_malloc(SIZE)时会发生以下神奇的事情集体操作: 所有 PE我们称之为PE 0,PE 1, …,PE N-1必须同时调用这个函数并且传入相同的SIZE。本地分配: 每个 PE 都会在自己的本地 GPU 显存上分配一块大小为SIZE的物理内存。地址映射:PE 0上的nvshmem_malloc会返回一个本地虚拟地址比如0xAAAA0000。PE 1上的nvshmem_malloc会返回一个本地虚拟地址比如0xBBBB0000。这些返回的虚拟地址rdma_buffer_ptr是不同的你不能直接把PE 0的0xAAAA0000发送给PE 1去用它不认识。“NVSHMEM 基地址”和“偏移量”这里的“NVSHMEM 基地址”是一个抽象的概念它并不一定是一个具体的、你可以直接看到的数值。你可以把它想象成 NVSHMEM 内部维护的一个全局地址簿的起点。当所有 PE 都调用了nvshmem_malloc(SIZE)之后NVSHMEM 库在内部会建立这样的映射关系PE ID本地虚拟地址rdma_buffer_ptr在 NVSHMEM 全局地址空间中的逻辑位置PE 00xAAAA0000逻辑基地址 0PE 10xBBBB0000逻辑基地址 SIZEPE 20xCCCC0000逻辑基地址 2 * SIZE………PE i(某个地址)逻辑基地址 i * SIZE现在我们来解释“相对于 NVSHMEM 基地址的偏移量完全相同”这句话。这其实是一种更通俗的说法其背后精确的含义是任何一个 PE 上的、由nvshmem_malloc分配的对称内存区域中的任意一个字节都可以通过(目标 PE_ID, 字节在目标 PE 本地对称内存中的偏移量)这个二元组被唯一地、全局地寻址。举个例子SymBuffer如何工作SymBuffer完美地利用了这一特性。假设SymBuffer的构造函数计算出要发送给rank m的channel_id c的data区域距离rdma_buffer_ptr的偏移量是OFFSET。在PE 0上:send_ptr指向的地址是0xAAAA0000 OFFSET。这是PE 0自己的发送区。当PE 0想获取PE 1的接收区地址时它调用SymBuffer::peer(1)。peer(1)函数内部会调用nvshmem_ptr(rdma_buffer_ptr OFFSET, 1)。nvshmem_ptr函数会执行翻译“我知道rdma_buffer_ptr是一个对称地址。”“用户想访问这个对称地址在PE 1上的实例。”“并且想访问这个实例内部OFFSET的位置。”NVSHMEM 库查询其内部的地址簿找到PE 1的对称内存基地址0xBBBB0000。它返回0xBBBB0000 OFFSET。这是一个在PE 0的地址空间中有效的虚拟地址但它通过 GPU 驱动和硬件GPUDirect RDMA被映射到了PE 1的物理显存上。总结NVSHMEM 基地址: 不是一个单一的物理地址而是一个逻辑概念代表了由一次nvshmem_malloc创建的所有对称内存区域在 NVSHMEM 全局地址空间中的“起点”。偏移量相同: 这句话的本质意思是对称内存的内部布局在所有 PE 上都是完全一致的。因此一个OFFSET在所有 PE 上都指向其本地对称内存中逻辑上相同的位置比如都是“与 rank m 通信的 channel c 的 data 区”。nvshmem_ptr的魔力: NVSHMEM 提供的 API (如nvshmem_ptr) 负责将(逻辑位置, 目标 PE)这个抽象请求翻译成一个可以在当前 PE 上直接使用的、指向远程 PE 物理内存的有效虚拟地址。正是这种设计使得SymBuffer能够仅通过计算本地的偏移量就能推断出如何获取到任何一个远程伙伴的、逻辑上对应的内存区域的直接访问指针从而实现了高效的 RDMA 通信。好的这张图非常清晰地解释了deep_ep中两个核心的内存访问辅助类SymBuffer和AsymBuffer的设计哲学和内存布局。它们都是为了在复杂的并行环境中简化对共享内存区域的访问。让我们来逐一解析。Buffer(顶层)buffer --offset-- ptr ----------- | num_bytes | -----------含义: 这是最基础的内存块概念。一个buffer由一个基地址指针ptr和一个大小num_bytes定义。offset表示这个buffer相对于某个更大的内存区域的起始偏移量。SymBuffer: 用于多个 SM 对一段内存的统一访问全称:Symmetric Buffer(对称缓冲区)。核心思想: 这类缓冲区通常用于节点间 (Inter-node)通信基于NVSHMEM分配的对称内存。对称内存意味着每个参与 RDMA 的rank都在自己的 GPU 上拥有一个虚拟地址不同、但相对于NVSHMEM基地址的偏移量完全相同的内存区域。内存布局:整个SymBuffer管理的内存被分为两个大的部分发送区 (蓝色)和接收区 (红色)。在每个区发送/接收内部又被划分为sm 0到sm k的区域这里的sm实际上更准确的理解应该是通道 (channel)。在每个通道的区域内部又被划分为rank 0到rank m的子块每个子块用于与一个特定的目标rank通信。指针解析:send_ptr: 指向本地的发送区域。当rank A想要发送数据给rank B时rank A上的 Kernel 会通过send_ptr找到为rank B预留的发送子块并将数据写入其中。recv_ptr: 指向远端的接收区域。rank A的 Kernel 在将数据写入本地send_ptr指向的区域后会发起一个 RDMAput操作将这些数据传输到rank B的recv_ptr指向的内存位置。为什么叫 “Symmetric”: 因为rank A的send_ptr加上某个偏移量在逻辑上就对应rank B的recv_ptr加上相同的偏移量。NVSHMEM负责处理这种虚拟地址到物理地址的映射。SymBuffer类封装了这种对称性使得开发者可以方便地获取本地发送地址和远程接收地址。AsymBuffer: 用于多个 SM 对多段内存的统一访问全称:Asymmetric Buffer(非对称缓冲区)。核心思想: 这类缓冲区通常用于节点内 (Intra-node)通信基于 CUDA 的 IPC (Inter-Process Communication) 机制。在这种模式下一个 GPU (比如rank A) 可以直接访问另一个 GPU (rank B) 的物理显存。内存布局:与SymBuffer不同AsymBuffer管理的内存不是一个连续的大块而是多个分散的、属于不同 GPU 的内存块的集合。图中的sm 0到sm k同样代表不同的通道。在每个通道内部rank 0到rank m的子块代表了与节点内伙伴rank通信所需的内存区域。关键区别: 这些rank 0...m的子块物理上位于不同的 GPU 上。例如rank 0的子块在 GPU 0 的显存里rank m的子块在 GPU m 的显存里。指针解析:ptr:AsymBuffer内部通常不只有一个ptr而是维护一个指针数组即我们之前讨论的buffer_ptrs_gpu。当AsymBuffer被创建时它会根据传入的目标rank从这个指针数组中选择正确的基地址然后再根据channel_id等计算出最终的精确地址。图中的ptr指向的是某个特定目标rank m的内存子块。当rank A要和rank B通信时AsymBuffer会帮助rank A的 Kernel 计算出指向rank B显存中对应缓冲区的直接访问指针。为什么叫 “Asymmetric”: 因为没有统一的发送区和接收区。通信是直接的“写入-你的内存”和“读取-我的内存”。rank A写入rank B的地址和rank B写入rank A的地址是完全不同的、非对称的。AsymBuffer类封装了这种通过buffer_ptrs_gpu数组进行地址查找和偏移计算的复杂逻辑。总结这张图通过对比清晰地阐明了两种缓冲区封装类的设计意图SymBuffer(对称):应用场景:节点间 RDMA 通信。底层内存: 单一、连续的NVSHMEM对称内存块。核心功能: 封装 “本地发送区 - 远程接收区” 的对称映射关系。简化了: RDMA 通信中复杂的远程地址计算。AsymBuffer(非对称):应用场景:节点内 NVLink/IPC 通信。底层内存: 多个、分散在不同 GPU 上的物理内存块。核心功能: 封装通过指针数组查找伙伴 GPU 内存地址的逻辑。简化了: CUDA P2P 通信中直接访问伙伴 GPU 内存的地址计算。通过提供这两种高级抽象deep_ep的 Kernel 代码可以忽略底层是 RDMA 还是 IPC 的差异用一套相似的接口如.self(),.peer(), 或直接通过构造函数获取目标指针来编写通信逻辑极大地提高了代码的可读性和可维护性。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价