核函数吞吐指标:Occupancy、Bandwidth Utilization、Compute Utilization

各位同学,今天我们来聊聊核函数吞吐的三个核心指标。说实话,我刚开始做GPU优化那会儿,看到Occupancy、带宽利用率、计算利用率这些词,总觉得是理论家搞出来的概念。直到有一次,我花了两周调一个kernel,性能死活上不去,最后发现是Occupancy虚高、带宽根本没跑满——嗯,从那以后我再也不敢小看这些指标了。

Occupancy:你到底用了几成力?

Occupancy,说白了就是SM上活跃的warp数量占理论最大值的比例。我习惯这么理解:GPU有好多计算单元,你同时塞进去的线程越多,隐藏延迟的能力就越强。

Occupancy = 活跃warp数 / 最大warp数

比如一个SM最多能放64个warp,你只放了32个,那Occupancy就是50%。

但这里有个坑——高Occupancy不等于高性能。我曾经调过一个矩阵乘法的kernel,Occupancy从50%拉到100%,性能反而下降了。为什么?因为每个线程用的寄存器太多,Occupancy高了,每个线程分到的资源反而少了,计算效率反而下降。

我个人习惯的做法是:先跑一个Occupancy扫描,看看不同block大小下的实际Occupancy。CUDA提供了现成的工具:

// 用CUDA Occupancy API计算理论Occupancy
int minGridSize, blockSize;
cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, 
                                    myKernel, 0, 0);
// blockSize就是推荐值,但别全信,还得实测

我的经验:Occupancy在50%-75%之间往往是最优区间。低于50%,延迟隐藏不够;高于75%,资源争抢严重。当然,具体还得看你的kernel是计算密集还是访存密集。

Bandwidth Utilization:你的显存跑满了吗?

带宽利用率,这是我最看重的指标之一。你想想看,GPU的显存带宽动辄几百GB/s,如果你的kernel只用了10%,那再高的Occupancy也是白搭。

怎么算?很简单:

Bandwidth Utilization = 实际带宽 / 理论峰值带宽

实际带宽可以用nvprof或者Nsight来抓。我记得有一次调一个图像处理的kernel,带宽利用率只有20%。我一看,每个线程只读一个float,然后写一个float,这访存模式太差了。

优化后,我让每个线程连续读4个float,再用向量化加载——带宽利用率直接飙到75%。

注意:带宽利用率高不一定就好。我曾经见过一个kernel,带宽利用率95%,但计算利用率只有5%。这说明它是个纯访存瓶颈的kernel,计算单元都在闲着。这时候你要考虑的是减少访存量,而不是继续优化带宽。

我一般把带宽利用率分成三个档次:

利用率范围 状态 建议
< 30% 很差 检查访存模式,考虑合并访问
30% - 70% 一般 可以优化,但先看看计算利用率
> 70% 良好 带宽接近瓶颈,考虑减少访存量

Compute Utilization:计算单元在忙啥?

计算利用率,说白了就是SM上的计算单元(CUDA Core、Tensor Core)有多少时间在干活。这个指标和带宽利用率是相辅相成的。

我习惯用Nsight Compute看两个东西:

  • 算术利用率(Arithmetic Utilization):计算指令占所有指令的比例
  • 流水线占用率(Pipeline Utilization):计算流水线有多少周期是满的

举个例子,我调过一个卷积kernel,计算利用率只有20%。一看profile结果,大部分时间花在了访存等待上。说白了,这是个访存瓶颈的kernel,计算单元都在等数据。

怎么优化?我当时的做法是:

  1. 先用shared memory缓存输入数据,减少全局访存
  2. 再用向量化加载,提高带宽利用率
  3. 最后调整block大小,让Occupancy和计算利用率匹配

优化后,计算利用率从20%涨到了65%,整体性能提升了3倍。

一个小技巧:如果你看到计算利用率低、带宽利用率也低,那大概率是Occupancy太低,或者kernel启动开销太大。这时候先别急着优化算法,先看看你的grid和block配置是不是合理。

三个指标的关系:一个三角平衡

这三个指标不是孤立的。我画了一张图,帮你理解它们的关系:

Occupancy Bandwidth Utilization Compute Utilization 三者平衡 才是最优性能 高Occupancy + 高带宽 + 高计算 = 理想状态

你看,这三个指标构成了一个三角。任何一个指标太低,都会拖累整体性能。我见过太多人只盯着Occupancy看,结果带宽利用率和计算利用率都惨不忍睹。

我个人习惯的做法是:

  • 先看带宽利用率——如果低于30%,先优化访存
  • 再看计算利用率——如果低于20%,考虑减少计算量或提高并行度
  • 最后看Occupancy——如果低于50%,调整block大小

当然,这只是个通用流程。具体问题还得具体分析。

总结一下:

  • Occupancy:衡量你用了多少线程资源
  • Bandwidth Utilization:衡量你用了多少显存带宽
  • Compute Utilization:衡量你用了多少计算能力
  • 三者要平衡,别只盯着一个指标

好了,这一章的内容就到这里。记住,指标只是工具,真正重要的是理解你的kernel在硬件上是怎么跑的。下次你调kernel的时候,不妨先看看这三个指标,再动手优化——相信我,能少走很多弯路。

想要更多资料,联系可可