前言 本篇文章只适合稍微有一些cuda基础的朋友阅读。
occupancy初步理解
occupancy中文翻译是占用率,一个SM中理论假如最大支持M个active warp(这里不理解什么是active warp可以参考这里), 而实际上因为各种原因只能支持N个,这里N/M就是occupancy。
occupancy具体分析
- 最大active warp如何获取
这里我们利用Nvidia T4这个机器来分析, T4中每个SM会切分为4个SMSP(参考这里),每个SMSP有8个warp slot,理论上一个SM最大则可以容纳32个warp,从显卡给出的实际数据来看,我们的分析是正确的。
- 实际active warp 如何获取
这里我们会根据上面两张图来解析如何计算SM中实际支持的active warp个数。上面我们说了由于种种原因导致实际occupancy并不能达到100%,这里原因主要是三个: - Threads Per Block
- Registers Per Thread
- Shared Memory Per Block
在这里我们假设kernel的三个配置分别是128,71,512。接下来我们分析为什么Occupancy是88%?
occupany的推导流程
我们知道一个warp包含32个thread, 一个block是一堆warp的集合,此外由于一个block不可切分,只能分配在一个SM上
, 所以我们一旦确定了一个SM中最多能放入多少个block, 就可以知道最多能有active warp了。
首先我们先看Allocated Resources,第一列代表资源。首先看第一行warps资源,当下每个block有4=128/32个warp, 而SM中最多支持32 warp(Max Warps per Multiprocessor), 所以如果只看warp的限制,那么最多只能放入8=32/4个block。
这里注意,如果每个block的warp是1的话,算出来虽然是32个block,但是根据图片2中Max Thread Blocks per Multiprocessor可以知道,一个SM中最多允许放入16个block,所以也不会超过16.
接下来看第二行寄存器资源,首先我们要看一下Physical Limit of GPU这张表,这个表里显示寄存器的分配粒度是基于warp, 最小尺寸是256字节,一个SM总共有65536字节。由于我们设置是每个线程71字节,那么一个warp就需要2272 = 71 * 32字节。而寄存器不零售,都是批发给warp,所以一个warp会分配9个Unit Size,也就是2304=9*256个字节。那么受限于sm中65536字节的限制,这些寄存器一共可以供应28 = 65536/2304个warp。我们知道目前每个block是4个warp , 所以考虑寄存器资源最后最多只能配置7 = 28/4 个bock。
最后我们看第三行,每个block需要512字节的share memory, 而每个sm中最多只有32768个字节,那么考虑share memory资源最后最多可以配置64个block。
综上,根据木桶效应,sm中最多只能配置7个block。 每个block由于有4个warp, 所以active warp有28个。occupancy = 28 / 32 = 88%
Occupancy实际运用
下图展示了实际运用中并不会完全达到我们计算的结果,比如计算的是50%,而实际只有35.32%, 原因比较多. 参考这里
- Unbalanced workload within blocks
If warps within a block do not all execute for the same amount of time, the workload is said to be unbalanced. This means there are fewer active warps at the end of the kernel, which is a problem known as “tail effect”. The best solution is to try having a more balanced workload among the warps in each block.
自己理解一下,首先要清楚实际occupancy的计算逻辑,每一个sm中的每一个warp slot都有一个硬件数据统计器,每一个时钟周期它都会记录一下warp slot中有一个warp, 比如一个sm中含有1个大小为8的warp slot, 在时钟周期0时,每个warp slot中有6个warp,在时钟周期1的时候还是6个,时钟周期2的时候还是6个,时钟周期3的时候运算截至,那么平均数active warp时(6+6+6)/3=6, 则实际occupancy就是6/8=75%, 这个可能就是理论值一样,但是如果三个时钟周期还没截止,有部分warp拖拉没执行完,比如时钟周期4还有两个,时钟周期5还有两个,时钟周期6截至,那么active warp=(6+6+6+2+2)/5=4.4, 则实际occupancy就是4.4/8=55%,可以看到由于个别沙雕warp执行速度太慢导致时钟周期4和周期5还两个warp在slot, 这就是block内的计算不平衡,也就是长尾效应,说白了就是block在时钟周期4和5还要等这两个warp没法退出sm, sm也无法在时钟周期4和5最大利用资源。所以给大家的建议还是尽可能让warp之间执行指令时间差不多
- Unbalanced workload across blocks
If blocks within a grid do not all execute for the same amount of time, this workload is also unbalanced, but the efficiency of the device can be improved without having to change to a more balanced workload. Launching more blocks will allow new blocks to begin as others finish, meaning the tail effect does not occur inside every block, but only at the end of the kernel. If there are not more blocks to launch, running concurrent kernels with similar block properties can achieve the same effect.
和上面类似的理解
- Too few blocks launched
The upper limit for active blocks per SM is determined by the theoretical occupancy, but that calculation does not account for a launch with fewer than that number of blocks per SM. The number of SMs on the device times the maximum active blocks per SM is called a “full wave”, and launching less than a full wave results in low achieved occupancy. For example, on a device with 15 SMs, and a configuration expecting 100% theoretical occupancy with 4 blocks per SM, a full wave would be 60 blocks. Launching only 45 blocks (assuming a balanced workload) will result in approximately 75% achieved occupancy.
这个就比较好理解,比如一个sm有4个warp slot, 每个slot有8个位置,那么sm共32个位置,假如理论计算一个sm可以放4个block,每个block用8个warp, occupancy正好时100%,但是实际你launchkernel时候只给了1个block且一个时钟周期结束, 按照硬件统计的话(8+0+0+0)/1=8个active warp, 占用率=8/32=25%。
- Partial last wave
The SM has a maximum number of warps that can be active at once. Since occupancy is the ratio of active warps to maximum supported active warps, occupancy is 100% if the number of active warps equals the maximum. If this factor is limiting active blocks, occupancy cannot be increased. For example, on a GPU that supports 64 active warps per SM, 8 active blocks with 256 threads per block (8 warps per block) results in 64 active warps, and 100% theoretical occupancy. Similarly, 16 active blocks with 128 threads per block (4 warps per block) would also result in 64 active warps, and 100% theoretical occupancy.
上面这段话解释一下,这里我们使用T4这个机器,有40个sm, 根据kernel计算得到每个sm有8个block, 然后一个机器的full ware是320个block, 而实际只有250个block, 这要算出来平均就是0.78125 = 250/320个full ware, occupancy应该时统计所有的sm的平均值,所以这个也会导致问题。
occupancy 有啥用
我们知道,入住的warp越多,每个周期可选的发射warp指令越多,由于计算和访存是不同的pipeline, 而且访存时钟周期较大,所以当A_warp在做访存的时候,如果有很多warp当时要做计算,那么延时就可以被隐藏起来,所以越多active warp一般
会带来好的收益。