1. GPU架构基础
各位同学,咱们今天聊聊GPU的“骨架”。说白了,你要压测一个核函数,得先知道它跑在什么上面。我见过太多人,代码写得飞起,结果一跑性能惨不忍睹,根本原因就是不懂硬件。
GPU不是一块简单的芯片。它是个并行怪兽。你得理解它的组织方式,才能驯服它。
CUDA核心与SM:从士兵到军团
先说说最基础的概念。CUDA核心,很多人叫它“流处理器”。我个人习惯把它想象成一个士兵。这个士兵能干活,但能力有限——它只能执行简单的算术逻辑运算。
那SM是什么?SM是Streaming Multiprocessor,流式多处理器。你可以把它理解成一个“连队”。一个连队里有很多士兵(CUDA核心),还有共享的指挥所(共享内存)、调度官(Warp Scheduler)和后勤仓库(寄存器文件)。
我刚开始接触GPU时,总以为CUDA核心越多越好。其实不然。你想想看,一个连队再强,如果调度官不行,士兵们也只能干等着。
关键点:一个SM包含多个CUDA核心。但真正决定并行效率的,是SM的数量和每个SM的资源配比。
举个例子。我曾在项目中优化过一个矩阵乘法的核函数。一开始,我拼命增加线程数,以为这样就能跑满GPU。结果呢?性能反而下降了。为什么?因为每个SM的资源是有限的。线程太多,每个线程分到的寄存器就少,数据不得不 spill 到本地内存,速度一下子就垮了。
嗯,这里要注意:线程数 ≠ 并行度。真正的并行度,取决于SM能同时调度多少个Warp。
Warp调度机制:GPU的节拍器
Warp是什么?它是GPU调度的基本单位。一个Warp包含32个线程。这32个线程,在同一个SM上,执行同一条指令。
我经常跟团队说:“Warp是GPU的心跳”。你写的每个核函数,最终都会被拆分成Warp来执行。
调度机制是这样的:SM里有多个Warp Scheduler。每个Scheduler每个周期能发射一条指令给一个Warp。但注意,不是所有Warp都能被调度。只有那些“就绪”的Warp——也就是数据已经准备好、没有依赖冲突的Warp——才能被选中。
为什么会这样?因为内存访问有延迟。你从全局内存读一个数,可能要等几百个周期。如果SM只有一个Warp,那这期间它就只能干等着。但如果有多个Warp,SM就可以切换到另一个Warp去执行,把等待时间隐藏起来。
说白了,这就是延迟隐藏。我做过一个压测,发现当每个SM的Warp数量少于16个时,性能会急剧下降。因为Warp太少,无法掩盖内存访问的延迟。
实战技巧:调优时,可以先用 cudaOccupancyMaxPotentialOccupancy 算一下你的核函数能达到多少占用率。占用率越高,SM能同时调度的Warp就越多,延迟隐藏效果越好。
我曾经踩过一个坑。有个核函数,我用了很多寄存器,结果每个SM能同时运行的Warp数量被限制住了。性能惨不忍睹。后来我减少了寄存器使用量,占用率从30%提升到了70%,吞吐量直接翻倍。
避坑指南:不要盲目追求高占用率。有些核函数计算密集,寄存器多反而能减少数据搬运。占用率只是个参考,最终还是要看实际吞吐量。
内存层次结构:GPU的存储体系
GPU的内存,不是铁板一块。它有层次结构,速度差异巨大。我画了一张图,帮你理清思路。
这张图你看懂了吗?从上往下,速度越来越慢,容量越来越大。
- 寄存器:最快,但每个线程只有有限个。我习惯把它当成“工作台”,当前计算的数据放这里。
- 共享内存:同一个Block内的线程可以共享。速度很快,但容量小。我经常用它来做数据复用,比如矩阵分块。
- L1/L2缓存:自动管理,不用你操心。但你要知道它的存在。访问模式不对,缓存命中率低,性能就上不去。
- 全局内存:最慢,但容量最大。所有线程都能访问。压测时,全局内存的带宽往往是瓶颈。
我的经验:写核函数时,先问自己三个问题:数据能不能复用?能不能用共享内存?访问模式是不是连续的?这三个问题想清楚了,性能至少提升50%。
举个例子。我优化过一个归约(Reduction)操作。一开始,每个线程直接读写全局内存,带宽利用率只有20%。后来我改成先用共享内存做部分归约,再写回全局内存。带宽利用率直接飙到80%以上。
为什么会这样?因为共享内存的延迟只有全局内存的几十分之一。而且,共享内存支持Bank Conflict,但那是另一个话题了。咱们后面再细聊。
注意:共享内存用多了,每个SM能同时运行的Block数量就会减少。这是个 trade-off。我一般会先用 cudaOccupancyMaxPotentialBlockSize 算一下最优配置。
总结一下
GPU架构的核心,就是SM、Warp和内存层次。你理解了这三样东西,压测和调优就有了方向。
我个人习惯,拿到一个新核函数,先看它的占用率,再看它的内存访问模式。如果占用率低,就减少寄存器或共享内存使用量。如果内存访问不连续,就调整数据布局或使用共享内存做转置。
嗯,今天就先聊到这儿。记住一句话:“让数据靠近计算,让Warp填满SM”。这是GPU优化的黄金法则。