理解 GPU 为什么快、什么时候不快 —— 这是写好 kernel 的前提。
CPU 把芯片面积大量投在分支预测、乱序执行和超大缓存上,为的是让**单条指令流**尽可能少停顿。GPU 反过来:大量简单的执行单元 + 极少的控制逻辑,靠**海量并发**把延迟藏起来。
这个选择决定了一切:GPU 不怕「慢」,怕「不齐」——只要同一批线程走不同分支,一部分单元就空转。
实践结论:让相邻线程处理相邻数据,天然避免分支发散 —— 这不只是为了访存合并,也是为了不浪费执行单元。
| 层级 | 典型容量 | 特点 |
|---|---|---|
| 寄存器 | 每线程几十个 | 最快,但用多了会限制 occupancy |
| 共享内存 / L1 | 每 SM 数十 KB | 同一 block 内共享,需手动管理 |
| L2 | 数 MB | 跨 SM 共享,容量有限 |
| 全局显存(HBM/GDDR) | 数十 GB | 带宽高但延迟也高,**合并访问是关键** |
全局显存的一次访问如果只用到 32 字节里的一部分,剩下的带宽就浪费了。**相邻线程访问相邻地址**(coalesced access)能让一次事务喂饱整个 warp。
Occupancy 指 SM 上实际驻留的 warp 数与上限之比。它高意味着有更多 warp 可以切换来掩盖延迟;但为了堆 occupancy 而减少寄存器使用,可能导致变量反复 spill 到本地内存 —— 反而更慢。
正确顺序:先让 kernel 正确、访存合并,再用 profiler 看瓶颈到底是延迟还是带宽,最后才调 occupancy。