4.16. 进程间通信
通过使用进程间通信(IPC)API 和可由 IPC 共享的内存缓冲区,可以支持由不同主机进程管理的多个 GPU 之间的通信,其方法是创建可跨进程使用的句柄,随后使用这些句柄获取指向对等 GPU 上设备内存的进程本地设备指针。
由主机线程创建的任何设备内存指针或事件句柄,都可以由同一进程中的任何其他线程直接引用。但是,设备指针或事件句柄在创建它们的进程之外无效,因此不能由属于不同进程的线程直接引用。要跨进程访问设备内存和 CUDA 事件,应用程序必须使用 CUDA 进程间通信(IPC)或虚拟内存管理 API,创建可跨进程使用的句柄,并通过标准主机操作系统 IPC 机制(例如进程间共享内存或文件)与其他进程共享这些句柄。在进程之间交换了可跨进程使用的句柄之后,必须使用 CUDA IPC 或 VMM API 从句柄获取进程局部的设备指针。随后,进程局部的设备指针就可以像在单个进程中一样使用。
在单个节点和单个操作系统实例中用于 IPC 的同类可移植句柄方法,也用于通过 NVLink 连接的多节点集群中 GPU 之间的对等通信。在多节点情况下,进行通信的 GPU 由集群中各节点上独立操作系统实例内运行的进程管理,因此需要在操作系统实例级别之上增加一层抽象。多节点对等通信的实现方式是:在多节点对等 GPU 之间创建和交换所谓的“互连网络(fabric)”句柄,然后在与多节点秩对应的参与进程和操作系统实例中获取进程局部的设备指针。
有关用于建立和交换可跨进程、跨节点和跨操作系统实例使用的句柄的具体 API,请参见下文(单节点 CUDA IPC)和 ref::virtual-memory-management;这些句柄用于获取 GPU 通信所需的进程局部设备指针。
注意
CUDA IPC API 和虚拟内存管理(VMM)API 用于 IPC 时,各有其优势和限制。
CUDA IPC API 目前仅在 Linux 平台上受支持。
CUDA 虚拟内存管理 API 允许在分配内存时,针对每次分配控制对等访问能力和共享,但要求使用 CUDA Driver API。
4.16.1. 使用旧版进程间通信 API 进行 IPC
要跨进程共享设备内存指针和事件,应用程序必须使用 CUDA 进程间通信 API,参考手册中对此 API 有详细说明。IPC API 允许应用程序使用cudaIpcGetMemHandle()获取给定设备内存指针的 IPC 句柄。CUDA IPC 句柄可以通过标准主机操作系统 IPC 机制(例如进程间共享内存或文件)传递给另一个进程。cudaIpcOpenMemHandle()使用 IPC 句柄获取可以在另一个进程内使用的有效设备指针。事件句柄可以使用类似的入口点进行共享。
使用 IPC API 的一个示例是,由单个主进程生成一批输入数据,使多个辅助进程能够使用这些数据,而无需重新生成或复制。
注意
IPC API 仅在 Linux 上受支持。
请注意,IPC API 不支持cudaMallocManaged分配的内存。
使用 CUDA IPC 相互通信的应用程序应使用相同的 CUDA 驱动程序和运行时进行编译、链接和运行。
出于性能原因,cudaMalloc()分配的内存可能是从较大的内存块中子分配而来的。在这种情况下,CUDA IPC API 会共享整个底层内存块,这可能导致其他子分配的内存也被共享,从而可能导致进程之间的信息泄露。为防止这种行为,建议只共享大小按 2MiB 对齐的内存分配。
在计算能力为 7.x 及更高版本的 L4T 和嵌入式 Linux Tegra 设备上,仅支持 IPC 事件共享 API。Tegra 平台不支持 IPC 内存共享 API。
4.16.2. 使用虚拟内存管理 API 进行 IPC
CUDA 虚拟内存管理 API 允许创建可通过 IPC 共享的内存分配,并借助特定于操作系统的 IPC 句柄数据结构来支持多种操作系统。