CPU一颗芯片就几个到十几个核心(一般),这些核心独自执行不同任务。GPU和它不同。GPU有几千个计算单元,但逻辑不是CPU那样能够独自执行不同任务,而是每32个一组,绑在一起同步执行同一条指令,只是处理的数据不同。
GPU的这种模式叫SIMT(单指令,多线程)。可以想象成一个车间,工头喊一句指令,32位工人同时执行这个动作,但每个人手里的物料不同,不能让其中的一部分人干这个,另一部分人干那个,要么全体一起执行,要么全体一起等待。
这32位工人(32个线程)捆在一起同步执行的单位,叫做warp。这是CUDA里面最基本的调度单位。
GPU芯片被分成很多个SM(流式多处理器)。每个warp必须完整地待在一个SM里面执行,不会被拆到两个SM上。SM在同一时刻能同时驻留很多个warp,但受硬件资源限制(寄存器数量、shared memory容量、warp scheduler数量),SM在同一个时钟周期能真正执行的warp数是有限的。
为什么要驻留远多于能同时执行的数量?答:延迟隐藏。
解释一下延迟隐藏。当一个warp在等显存数据返回可能要几百个时钟周期,SM会立刻切换执行另一个已经准备好的warp,而不是干等着。这也是occupancy(占用率)这个概念的来源,它表示的是SM里驻留的warp数量相对最大值的比例。occupancy太低,SM没有足够的备用warp去填补等待显存的空档,就会浪费计算能力干等着。
下面列出显存的三个层级
| 层级 | 大致速度 |
|---|---|
| 寄存器 | ~1个时钟周期 |
| shared memory | 几个时钟周期 |
| global memory | 几百个时钟周期 |
shared memory的作用范围是block(这是CUDA编码时的其中一层结构)。不同block可能跑在不同的SM上,物理上是隔离的,所以彼此看不到对方的shared memory。
访问global memory时,访问模式对性能影响巨大。如果一个warp里32个线程访问的地址是连续、对齐的,硬件可以把这32次读取合并成一次“批发”操作(coalesced access);如果地址跳跃很大,硬件可能要发起很多次独立的显存事务,代价高出几倍到十几倍。
为什么标题说是“简单”了解呢?因为上面的不用动脑。最近在搞别的事情,脑子不够用,暂时就到这儿。谢谢。