GPU架构基础:核心组成、流多处理器(SM)、线程束(Warp)、内存层次结构、GPU计算能力
好,咱们进入第二讲。这一讲我打算把GPU的“骨架”给你拆开看看。说白了,你得知道手里这把“异构计算”的刀,到底长什么样、怎么磨的。
很多初学者一上来就学CUDA编程,结果写出来的代码跑得比CPU还慢。为什么?因为根本不了解GPU的脾气。我当年就吃过这个亏,一个简单的向量加法,优化了一周,性能纹丝不动。后来才发现,是我对SM和Warp的理解太肤浅了。
GPU核心组成:一个“大管家”和一群“打工人”
先看整体。一个GPU芯片,你可以把它想象成一个超级工厂。
- 全局调度器(GigaThread Engine):这是大管家。它负责把任务(线程块)分配给下面的各个车间。
- 流多处理器(SM):这是车间。每个车间里有一堆工人(CUDA核心)和共享的工具箱(共享内存)。
- 内存控制器:这是仓库管理员。负责从显存(全局内存)里搬货。
我个人习惯把GPU看作一个“延迟隐藏机器”。它不是为了把单次计算做快,而是为了同时处理海量任务,让计算单元永远不闲着。
流多处理器(SM):GPU的心脏
SM是GPU里最重要的计算单元。一个GPU里有多少个SM,基本决定了它的算力上限。
每个SM内部包含:
- CUDA核心:执行整数和浮点运算的单元。注意,它们不是独立的CPU,而是高度流水线化的执行单元。
- 共享内存(Shared Memory):SM内部的高速缓存,由程序员手动管理。速度极快,但容量很小(通常几十KB到上百KB)。
- 寄存器文件(Register File):每个线程私有的“草稿纸”。容量有限,如果每个线程用太多寄存器,会限制SM上同时运行的线程数。
- Warp调度器:负责从就绪的Warp中挑选一个,发射指令到CUDA核心。
- 加载/存储单元(LD/ST):处理内存访问请求。
- 特殊函数单元(SFU):处理sin、cos、sqrt等复杂数学运算。
关键点:SM是独立的。每个SM有自己的寄存器、共享内存和调度器。线程块一旦被分配给某个SM,就会一直待在那里,直到执行完毕。这叫“驻留”。
我在项目中遇到过一个问题:一个kernel里用了太多共享内存,结果每个SM只能同时运行一个线程块。SM的利用率直接掉到谷底。嗯,这就是典型的“资源限制”案例。
线程束(Warp):GPU执行的最小单位
这是GPU架构里最反直觉、也最重要的概念。
你写代码时,觉得是32个线程在并行执行。但在硬件眼里,这32个线程被捆绑成一个Warp,一起取指令、一起执行。
Warp是怎么工作的?
- 一个Warp包含32个线程。这32个线程共享同一个程序计数器(PC)。
- Warp调度器每次发射一条指令,Warp里的所有线程都执行这条指令。
- 如果线程们走的分支不同(比如if-else),就会发生“Warp发散”。
避坑指南:我曾经写过一个kernel,里面有个if语句,一半线程走true分支,一半走false分支。结果性能直接腰斩。因为Warp必须串行执行两个分支,先执行true的16个线程,再执行false的16个线程。你想想看,这等于浪费了一半的算力。
所以,写GPU代码时,要尽量保证同一个Warp里的线程走相同的分支。说白了,就是让它们“步调一致”。
内存层次结构:速度与容量的博弈
GPU的内存系统,是一个典型的金字塔结构。越往上,速度越快,容量越小。
| 内存类型 | 位置 | 速度 | 容量 | 作用域 |
|---|---|---|---|---|
| 寄存器 | SM内部 | 最快(1个周期) | 非常小(每个线程几十个) | 线程私有 |
| 共享内存 | SM内部 | 快(几个周期) | 小(几十KB) | 线程块内共享 |
| L1缓存 | SM内部 | 较快 | 几十KB | SM内所有线程 |
| L2缓存 | 芯片上 | 中等 | 几MB | 所有SM共享 |
| 全局内存(显存) | 芯片外 | 慢(几百个周期) | 大(几GB到几十GB) | 所有线程 |
| 本地内存 | 芯片外(显存) | 慢 | 由编译器决定 | 线程私有(寄存器溢出时使用) |
这里有个常见的误解:很多人以为L1缓存和共享内存是独立的。其实在NVIDIA的架构里,它们共享同一块物理SRAM。你可以通过编译器指令来分配比例。比如,你可以配置48KB共享内存+16KB L1,或者反过来。
我的经验:如果kernel里需要频繁的线程间通信,我会把更多的SRAM分配给共享内存。如果只是数据复用,我会倾向于多给L1缓存。这个取舍,得根据具体算法来定。
GPU计算能力:一张“能力清单”
NVIDIA用“计算能力”(Compute Capability)来标识GPU的硬件特性。比如,RTX 3090的计算能力是8.6,A100是8.0。
这个数字很重要。它决定了:
- 每个SM最多能驻留多少个Warp
- 每个线程块最多能有多少个线程
- 共享内存的最大容量
- 是否支持某些特殊指令(比如Tensor Core)
举个例子,计算能力7.0(V100)的GPU,每个SM最多能驻留64个Warp(2048个线程)。而计算能力8.0(A100)的GPU,这个数字变成了64个Warp,但每个Warp的调度效率更高。
我建议你在写代码前,先查一下目标GPU的计算能力。NVIDIA官方文档里有个表格,列出了所有计算能力对应的硬件参数。这个表格,我几乎每做一个新项目都要翻一遍。
一句话总结:GPU架构的核心,就是SM、Warp和内存层次结构这三板斧。理解了它们,你就理解了GPU为什么能并行、为什么会有性能瓶颈、以及该怎么优化。
下一讲,我们会深入Warp的调度机制,聊聊“延迟隐藏”到底是怎么实现的。到时候,你会对GPU的“并发”能力有更直观的认识。