https://www.nvidia.com/zh-tw/data-center/nvlink/
这不只是一种软件技术,实现这样的高带宽也是需要特定硬件支持的。我的理解:这是一种特殊的芯片,内嵌在某些特殊定制的主板上,显卡直接和这个芯片连接(而不是普通的PCIe接口),就能提高传输速度(并不是印象中「一根线」的样子)
[zh-ge@flogger cudastf]$ nvidia-smi topo -p2p r
GPU0 GPU1 GPU2 GPU3
GPU0 X OK OK OK
GPU1 OK X OK OK
GPU2 OK OK X OK
GPU3 OK OK OK X
Legend:
X = Self
OK = Status Ok
CNS = Chipset not supported
GNS = GPU not supported
TNS = Topology not supported
NS = Not supported
U = Unknown
一个P2P数据传输测速的程序:https://github.com/GZhonghui/cuda_samples/blob/master/09_p2p_speed_test/p2p_speed_test.cu
注意:
[zh-ge@flogger cudastf]$ nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 CPU Affinity NUMA Affinity GPU NUMA ID
GPU0 X PIX NODE NODE 0-9,20-29 0 N/A
GPU1 PIX X NODE NODE 0-9,20-29 0 N/A
GPU2 NODE NODE X PIX 0-9,20-29 0 N/A
GPU3 NODE NODE PIX X 0-9,20-29 0 N/A
Legend:
X = Self
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
PIX = Connection traversing at most a single PCIe bridge
NV# = Connection traversing a bonded set of # NVLinks
要在一台主机上使用 CUDA 让程序同时利用多个 NVIDIA GPU 的计算能力,您可以采用以下步骤:
获取可用 GPU 数量:首先,使用 cudaGetDeviceCount 函数确定系统中可用的 CUDA 设备数量。
int deviceCount; cudaError_t status = cudaGetDeviceCount(&deviceCount); if (status != cudaSuccess) { // 处理错误 }
为每个 GPU 分配主机线程:为每个 GPU 分配一个主机线程,每个线程负责一个 GPU 的计算任务。可以使用多线程库(如 pthreads 或 OpenMP)来实现。
#pragma omp parallel for num_threads(deviceCount) for (int i = 0; i < deviceCount; ++i) { cudaSetDevice(i); // 在设备 i 上执行 CUDA 操作 }
管理设备上下文:在每个线程中,调用 cudaSetDevice 函数设置当前线程对应的设备。所有后续的 CUDA 操作将在该设备的上下文中执行。
数据分配与传输:为每个 GPU 分配独立的内存,并将相应的数据传输到各自的设备。
内核启动与同步:在每个 GPU 上启动内核进行计算。计算完成后,确保在主机端进行必要的同步,以收集和处理结果。
GPU 间通信(如有必要):如果不同 GPU 之间需要交换数据,尽量避免通过主机内存进行数据传输。对于支持对等访问(Peer-to-Peer, P2P)的 GPU,可以使用 CUDA 的 P2P 内存传输功能,实现直接的设备间通信。
int canAccessPeer; cudaDeviceCanAccessPeer(&canAccessPeer, device1, device2); if (canAccessPeer) { cudaSetDevice(device1); cudaDeviceEnablePeerAccess(device2, 0); // 现在 device1 可以直接访问 device2 的内存 }
性能优化:在多 GPU 编程中,性能优化至关重要。应注意以下几点:
避免不必要的主机与设备之间的数据传输:频繁的数据传输会导致性能下降,应尽量减少主机与 GPU 之间的数据交换。
使用流和异步操作:利用 CUDA 流和异步操作,可以实现数据传输与计算的重叠,提高效率。
负载均衡:确保每个 GPU 执行的工作量均衡,以防止某些 GPU 过载,而其他 GPU 空闲。
通过以上步骤,您可以在单台主机上有效地利用多个 GPU 的计算能力,从而提升 CUDA 程序的性能。