2、GPU架构基础:CUDA核心与SM、内存层次结构(Global/Shared/Register)、PCIe与NVLink互联

好,咱们正式开始聊多GPU集群之前,得先把单卡上的那点事儿捋清楚。说白了,你连一个GPU内部怎么干活都不知道,后面搞多卡通信那就是瞎忙活。这一节,我带你看看GPU的“五脏六腑”。

2.1 CUDA核心与SM:到底谁在干活?

很多人一上来就问:“GPU有多少个核心?” 嗯,这个问题其实有点坑。你看到的那些几千上万个“核心”,跟我理解的CPU核心不是一回事。

在NVIDIA的GPU里,最小的执行单元叫 CUDA Core。但真正独立调度、拥有完整指令流水线的,是 SM(Streaming Multiprocessor,流式多处理器)。你可以把SM想象成一个车间,CUDA Core就是车间里的工人。

关键认知: 一个SM包含多个CUDA Core(比如几十个到上百个不等),还有共享内存、寄存器文件、调度器、缓存等。程序是跑在SM上的,不是直接跑在单个CUDA Core上的。

我记得刚入行时,有个同事写了个kernel,以为线程数越多越好,结果性能反而下降了。为什么?因为他没搞懂 Warp 的概念。SM调度的最小单位是Warp,通常是32个线程一组。同一个Warp里的线程,必须执行同一条指令(SIMT模型)。如果分支发散,那性能就惨了。

避坑指南: 我曾经在优化一个粒子模拟程序时,发现分支判断导致Warp内线程走了不同路径。后来我把数据按条件重新排序,让同一个Warp里的线程执行相同分支,性能直接翻倍。记住:Warp内无分支,这是写CUDA代码的黄金法则之一。

每个SM能同时驻留多少个Warp?这取决于寄存器资源和共享内存的消耗。你想想看,一个SM的寄存器文件是有限的(比如65536个寄存器),每个线程分得越多,能同时跑的线程就越少。这就是所谓的 Occupancy(占用率)

2.2 内存层次结构:Global / Shared / Register

GPU的内存,说白了就是个金字塔。越往上越快,但越小。我习惯把这三层记成:大、慢、远 vs 小、快、近

内存类型 位置 速度 容量 作用域
Global Memory 显存(DRAM) 慢(几百周期) 大(GB级) 所有线程
Shared Memory SM内部(SRAM) 快(几十周期) 小(几十KB~几百KB) 同一个Block内的线程
Register SM内部(寄存器文件) 极快(1周期) 极小(每个线程几十个) 单个线程

Global Memory 就是咱们常说的显存。所有线程都能读写,但延迟高得吓人。我建议你尽量合并访问(coalesced access),让相邻线程访问相邻地址,这样显存控制器才能一次搬一大块数据过来。

Shared Memory 是个好东西。同一个Block里的线程可以共享数据,延迟比Global低一个数量级。我在做矩阵乘法优化时,就是靠Shared Memory做分块(tiling),把数据从Global搬到Shared里,然后反复用,性能提升非常明显。

注意: Shared Memory虽然快,但容量有限。而且要注意 Bank Conflict。如果多个线程同时访问同一个Bank的不同地址,就会串行化。我曾经调试一个程序,发现性能瓶颈就在Bank Conflict上,调整了数据布局后,速度提升了30%。

Register 是最快的,但每个线程能用的寄存器数量有限。如果你在kernel里声明太多局部变量,编译器会 spill(溢出)到Local Memory(其实还是Global Memory),那性能就崩了。我个人习惯是:能用寄存器就别用Shared,能用Shared就别用Global

2.3 PCIe与NVLink互联:GPU怎么跟外界说话?

单卡再强,数据进不去也是白搭。GPU跟CPU通信,或者GPU之间通信,靠的就是这两条路:PCIeNVLink

PCIe 是通用总线,所有显卡都支持。但它的带宽有限,延迟也高。比如PCIe 4.0 x16,理论带宽也就32GB/s(双向64GB/s)。你想想看,一个A100的显存带宽是2TB/s,PCIe这点带宽根本不够塞牙缝的。

NVLink 是NVIDIA自家的高速互联技术。它直接连接GPU到GPU,或者GPU到CPU(比如IBM Power9)。NVLink的带宽比PCIe高得多,延迟也更低。比如NVLink 3.0,每个链路带宽是50GB/s(单向),一个GPU可以有多个链路。

核心区别: PCIe走的是树形拓扑,所有设备共享带宽。NVLink走的是点对点直连,带宽独享。说白了,PCIe像一条主干道,车多了就堵;NVLink像多条高速公路,各走各的。

我记得在搭建一个8卡DGX-1集群时,发现GPU之间通过NVLink通信,延迟比PCIe低了将近一半。尤其是在做AllReduce操作时,NVLink的拓扑优势非常明显。如果你用PCIe做多卡通信,那瓶颈基本就在总线上。

避坑指南: 我曾经以为只要用了NVLink,通信就万事大吉了。结果发现,如果数据没有正确放置在 统一内存(Unified Memory) 或者显存里,NVLink也救不了你。记住:NVLink只加速显存之间的直接访问,不加速CPU到GPU的拷贝

下面这张图,是我自己画的GPU内部结构示意,帮你把这几层关系理清楚:

GPU架构核心结构图 CPU PCIe 总线 带宽: 32GB/s (PCIe 4.0 x16) GPU (显存: Global Memory) SM 0 CUDA Core x 64 Shared Memory Register File SM 1 CUDA Core x 64 Shared Memory Register File SM 2 CUDA Core x 64 Shared Memory Register File ... NVLink: 50GB/s 每链路 图例 SM (流式多处理器) Shared Memory Register File

嗯,这张图里,CPU通过PCIe连到GPU,GPU内部有多个SM,每个SM里有CUDA Core、Shared Memory和Register。SM之间通过NVLink直连(在多卡场景下)。说白了,你要优化的目标就是:让数据尽量待在Register和Shared里,少去Global,更少走PCIe

最后说一句,NVLink虽然快,但它不是万能的。它只解决GPU之间的通信瓶颈,不解决计算瓶颈。你想想看,如果算法本身是计算密集型的,那通信再快也没用。所以,先优化计算,再优化通信,这是我个人的经验之谈。