1. CUDA多卡编程基础:GPU拓扑结构认知、多卡通信模式概览、NVIDIA-SMI与nvidia-smi topo -m命令详解
1.1 为什么多卡编程要先看拓扑?
做多卡编程,第一件事不是写代码。
我个人的习惯是,先跑一遍 nvidia-smi topo -m,看看我的GPU们是怎么连在一起的。你想想看,如果两块卡之间走的是PCIe,另一对走的是NVLink,那通信带宽能差一个数量级。不知道这个信息,你写的多卡代码可能从一开始就输在起跑线上。
核心观点:GPU拓扑结构决定了多卡通信的物理路径和带宽上限。不了解拓扑,就无法做正确的通信模式设计。
1.2 GPU拓扑结构认知
什么是GPU拓扑?说白了,就是你的GPU在服务器里怎么摆放、怎么连接。
常见的拓扑结构有这几种:
- PCIe树形拓扑:所有GPU挂在PCIe Switch下面,通过CPU的PCIe控制器通信。这是最老、最慢的方式。
- NVLink全互联拓扑:每个GPU通过NVLink直接连到其他GPU,形成全连接网络。这是NVIDIA DGX系列的做法。
- 混合拓扑:部分GPU之间走NVLink,部分走PCIe。比如一些4卡或8卡的服务器。
我在项目中遇到过一台8卡服务器,看起来是8块A100,但跑多卡训练时发现通信特别慢。一查拓扑,发现只有4块卡之间有NVLink,另外4块只能走PCIe。嗯,这就是典型的混合拓扑坑。
为了让你更直观地理解,我画了一张图:
1.3 多卡通信模式概览
知道了拓扑,接下来要看通信模式。多卡编程里,GPU之间怎么交换数据?
主要有三种模式:
- 点对点通信(P2P):两块GPU直接读写对方显存。前提是它们之间有物理直连路径(比如NVLink或同一PCIe Switch下)。
- 集合通信(Collective):多块GPU一起做AllReduce、AllGather等操作。这是分布式训练的核心。
- 通过CPU中转:GPU先把数据拷到CPU内存,再拷到另一块GPU。这是最慢的方式,能避免就避免。
我的经验:能用P2P就别走CPU中转。我在一个项目里发现,两块卡之间走P2P延迟只有2微秒,走CPU中转直接飙到50微秒。差了25倍!
1.4 NVIDIA-SMI 命令详解
nvidia-smi 是每个做GPU编程的人最常用的命令。但很多人只用来看看显存占用和温度,其实它能看的东西远不止这些。
我常用的几个参数:
| 命令 | 作用 | 我的用法 |
|---|---|---|
nvidia-smi |
查看GPU基本信息 | 看显存、温度、功率、进程 |
nvidia-smi -q |
查询所有GPU详细信息 | 调试时看时钟频率、PCIe链路状态 |
nvidia-smi -l 1 |
每秒刷新一次 | 监控训练过程中的GPU状态变化 |
nvidia-smi pmon -c 1 |
进程监控模式 | 看哪个进程在占用GPU |
注意:我曾经在排查性能问题时,发现一块GPU的PCIe链路宽度只有x8而不是x16。用 nvidia-smi -q -d PCIE 一看,果然是物理连接有问题。这种问题不查nvidia-smi根本发现不了。
1.5 nvidia-smi topo -m 命令详解
这个命令是我做多卡编程时必用的。它输出GPU之间的拓扑关系矩阵。
直接看一个例子:
$ nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 CPU Affinity NUMA Affinity
GPU0 X NV2 NV2 PHB 0-31 N/A
GPU1 NV2 X PHB PHB 0-31 N/A
GPU2 NV2 PHB X NV2 32-63 N/A
GPU3 PHB PHB NV2 X 32-63 N/A
这个矩阵怎么看?我来解释一下:
- X:自己连自己,忽略
- NV2:通过2条NVLink连接。这是最快的路径。
- PHB:通过PCIe Host Bridge连接。速度慢一些。
- PXB:通过PCIe Switch连接。比PHB还慢一点。
- NODE:跨NUMA节点通信。最慢的情况。
你看上面的例子,GPU0和GPU1之间是NV2,说明它们可以直接高速通信。但GPU1和GPU2之间是PHB,只能走PCIe。如果你写多卡代码时没注意这个,把频繁通信的任务分配给了GPU1和GPU2,那性能肯定上不去。
避坑指南:我曾经在一个8卡训练任务中,默认用了GPU 0-7的连续编号。结果发现GPU 4和GPU 5之间是PHB连接,通信成了瓶颈。后来我手动把通信密集的GPU对分配到同一NVLink域内,训练速度提升了30%。
1.6 如何用拓扑信息指导编程
知道了拓扑,怎么用?我总结了几条原则:
- 通信密集的任务:优先分配给NVLink连接的GPU对
- 数据并行训练:尽量让同一节点内的GPU做AllReduce,跨节点走网络
- 流水线并行:把相邻流水线阶段放在通信快的GPU上
- 模型并行:把需要频繁交换梯度的层放在同一NVLink域
嗯,这些原则说起来简单,但实际项目中要结合你的具体场景来调整。没有银弹。
1.7 本章小结
这一章我们讲了三个核心内容:
- GPU拓扑结构:知道你的GPU怎么连的
- 多卡通信模式:P2P、集合通信、CPU中转
- nvidia-smi和topo -m命令:怎么查看和解读拓扑信息
说白了,多卡编程的第一步不是写代码,而是搞清楚你的硬件环境。拓扑决定了通信的上限,而你的代码只能逼近这个上限,不可能超越它。
下一章,我们会深入P2P通信的编程实现。到时候你会看到,同样的代码在不同拓扑下跑出来的效果天差地别。