4、GPU瓶颈分析:如何用工具定位是CPU还是GPU拖了后腿

高刷屏下掉帧,最让人头疼的问题就是——不知道谁在拖后腿。

CPU说“我负载不高啊”,GPU说“我也没跑满”。结果就是帧率上不去,卡顿一卡一卡的。我早年调一个120Hz项目时,就吃过这个亏。当时盯着CPU占用率看了三天,优化了个寂寞。后来才发现,是GPU的片段着色器太复杂,把渲染管线堵死了。

所以这一章,咱们就专门聊聊:怎么用工具,快速揪出瓶颈到底在CPU还是GPU。

4.1 为什么不能只看CPU占用率?

很多人习惯打开系统监视器,看一眼CPU占用率。如果不到80%,就觉得CPU没问题。这其实是个大坑。

你想想看,CPU占用率低,不代表它没在等。渲染管线里,CPU提交绘制命令给GPU,然后CPU可能就闲着等GPU完成。这时候CPU占用率当然低,但瓶颈在GPU啊。

反过来,GPU占用率低,也可能是CPU提交命令太慢,GPU饿着了。所以单看一个指标,根本说不清。

核心原则:瓶颈永远在“排队”的那一端。谁在等,谁就是瓶颈。

4.2 我的三板斧工具

我个人习惯用三个工具组合分析。不复杂,但够用。

工具 定位 看什么
Android GPU Inspector (AGI) GPU端详细分析 渲染阶段耗时、着色器执行、带宽
Systrace / Perfetto CPU端 + 整体流水线 Vsync信号、主线程、渲染线程、GPU等待
GPU Counters (Adreno/ Mali Tool) 硬件级计数器 ALU负载、纹理单元、ROP、总线读写

这三个工具,基本覆盖了从CPU到GPU的全链路。下面我一个个说怎么用。

4.3 第一步:用Systrace看“谁在等谁”

Systrace(现在叫Perfetto)是我最常用的起点。它能把CPU和GPU的活动画在一条时间轴上,一目了然。

具体怎么看?我教你一个口诀:找“空隙”。

  1. 抓取一段掉帧的trace。
  2. 找到掉帧的那个Vsync周期。
  3. 看CPU的“主线程”和“RenderThread”在干嘛。
  4. 看GPU的“SurfaceFlinger”或“hwui”阶段。

如果CPU早早干完活,然后出现一大段“等待GPU完成”的标记,那就是GPU慢了。

如果CPU一直在忙,直到Vsync前最后一刻才提交,那就是CPU瓶颈。

小技巧:在Perfetto里,按住'w'键放大,用鼠标拖拽。重点关注“sf”和“app”进程下的“doFrame”和“draw”阶段。我曾经靠这个,发现一个项目里CPU在等一个锁,白白浪费了3ms。

4.4 第二步:用AGI深挖GPU内部

Systrace告诉你“GPU慢了”,但没告诉你“GPU哪里慢了”。这时候就该AGI上场了。

AGI能抓到每一帧的GPU指令,拆解成几个阶段:

  • Vertex Shader(顶点着色器):处理顶点数据。瓶颈通常出现在模型顶点太多。
  • Rasterization(光栅化):把三角形变成像素。瓶颈通常出现在Overdraw(过度绘制)。
  • Fragment Shader(片段着色器):计算每个像素的颜色。瓶颈通常出现在复杂的光照、后处理特效。
  • Output Merging(输出合并):写入帧缓冲。瓶颈通常出现在带宽或显存。

打开AGI,加载trace后,直接看“Pipeline”视图。它会用柱状图显示每个阶段的耗时。哪个柱子最高,哪个就是瓶颈。

注意:AGI抓取时,手机可能会明显变慢。这是正常的,因为它在记录大量数据。抓取时间控制在3-5秒就够了,别贪多。

4.5 第三步:用GPU Counters做定量分析

有时候,AGI的Pipeline视图还不够细。比如它告诉你“片段着色器慢”,但你没改代码,不知道具体是哪里慢。这时候,硬件计数器能给出更精确的数据。

以高通Adreno GPU为例,用Snapdragon Profiler或Adreno GPU SDK里的工具,可以读到这些计数器:

计数器 含义 如果过高
ALU Busy 算术逻辑单元忙碌率 着色器计算太复杂
Texture Busy 纹理单元忙碌率 纹理采样太多或纹理太大
ROP Busy 渲染输出单元忙碌率 带宽瓶颈或分辨率太高
Shader Cycles 着色器执行周期数 分支太多或指令太长

举个例子。有一次我调一个游戏界面,帧率死活上不去。AGI显示片段着色器耗时高,但代码看起来不复杂。打开计数器一看,Texture Busy接近100%。原来是一张背景图用了4K分辨率,每帧都要采样,把纹理单元堵死了。换成2K图后,帧率直接翻倍。

4.6 一个实战判断流程

说了这么多工具,怎么串起来用?我画了个流程图,你可以照着走一遍。

GPU瓶颈定位判断流程 发现掉帧/卡顿 用Systrace看CPU vs GPU等待关系 CPU在等GPU? 是 用AGI分析GPU管线 否 优化CPU端(主线程/渲染线程) 用GPU Counters定位具体单元 针对性优化(纹理/着色器/带宽) 说明 CPU瓶颈: 优化布局、减少measure/layout

这个流程我用了好几年,基本没失手过。核心就是:先看谁在等,再看哪里慢,最后看为什么慢。

4.7 避坑指南

最后,分享几个我踩过的坑。

  • 别只看平均值。平均帧率60fps,但可能每10帧里有一帧卡了50ms。要看帧时间分布,看P99或P95。
  • 注意降频。手机玩一会儿就发热,GPU自动降频,性能直接腰斩。我遇到过好几次,以为是代码问题,结果是散热问题。抓trace时,记得看一眼温度传感器。
  • 驱动版本。同一个GPU,不同驱动版本,性能表现可能差很多。我曾经在某个项目里,升级驱动后,帧率提升了15%。所以,先确认驱动版本是不是最新的。
  • 别忽略总线。有时候GPU和CPU都不忙,但帧率就是上不去。可能是内存带宽不够,或者总线拥堵。用GPU Counters里的“Bus Read/Write”可以看出来。

我的习惯:每次调优前,先拍一张“性能快照”——记录当前帧率、温度、CPU/GPU频率、各阶段耗时。改完代码后,再拍一张。对比着看,效果一目了然。没有基线数据,优化就是瞎蒙。

好了,这一章的内容就到这里。工具和方法都给你了,剩下的就是动手去抓trace、看数据。记住,不要猜,要测。