第三章 性能分析工具链:Perfetto、DS-5 Streamline、MTK特有的GPU调优工具

做GPU性能优化,说白了就是一场「找瓶颈」的游戏。你找不到瓶颈在哪,再牛的优化技巧也是白搭。我在MTK平台上摸爬滚打这几年,用过不下十种性能分析工具,但真正能打的核心工具,其实就这三板斧。

今天我就把这套工具链的用法和心得,掰开了揉碎了讲给你听。

3.1 Perfetto:系统级trace的瑞士军刀

Perfetto是Google主推的下一代性能追踪工具。它取代了老旧的systrace,功能强了不止一个量级。我个人习惯把它当作「第一现场勘查工具」——先跑一遍Perfetto,看看整体情况再说。

3.1.1 核心能力

Perfetto能干什么?说白了就是记录系统里「谁在什么时候干了什么」。它能看到:

  • CPU每个核的调度情况
  • GPU的渲染管线状态
  • SurfaceFlinger的合成过程
  • App的线程活动和锁竞争
  • 内存分配和I/O操作

嗯,这里要注意:Perfetto的trace数据量可以非常大。我曾经在项目里开了一分钟trace,结果生成了2GB的数据文件。所以抓trace的时候,要有针对性,别一股脑全开。

3.1.2 实战用法

抓取GPU相关的trace,我一般这么配:

# 抓取GPU和显示相关的trace
adb shell perfetto \
  -c - --txt \
  <<EOF
buffers: {
    size_kb: 524288
    fill_policy: RING_BUFFER
}
data_sources: {
    config {
        name: "android.gpu.memory"
    }
}
data_sources: {
    config {
        name: "android.hwcomposer"
    }
}
data_sources: {
    config {
        name: "linux.scheduler"
    }
}
duration_ms: 5000
EOF
我的小技巧: 抓trace时把duration_ms设成5秒就够了。太短抓不到问题,太长数据量爆炸。5秒刚好能覆盖一次完整的滑动操作或动画过渡。

3.1.3 怎么看GPU问题

打开Perfetto的UI界面后,我一般先看这几个关键指标:

  • SurfaceFlinger的合成帧率——如果掉到30fps以下,说明合成层出问题了
  • GPU渲染时长——单帧超过16ms,那就是GPU瓶颈
  • CPU调度延迟——GPU在等CPU喂数据,这是常见的性能陷阱

为什么会这样?很多时候你以为GPU慢,其实是CPU没及时把数据准备好。我在一个项目中就遇到过这种情况:GPU利用率只有40%,但帧率就是上不去。一查Perfetto才发现,是CPU的渲染线程被调度到了小核上,导致数据提交延迟。

3.2 DS-5 Streamline:深入GPU内部

如果说Perfetto是「宏观侦察」,那DS-5 Streamline就是「微观解剖」。它是Arm官方提供的性能分析工具,对Mali GPU的支持尤其深入。

3.2.1 能看什么

Streamline能拿到GPU内部的硬件计数器数据,包括:

计数器名称 含义 正常范围
GPU Fragment Active 片元着色器活跃周期 < 80%
GPU Vertex Active 顶点着色器活跃周期 < 60%
External Bus Read 外部总线读取带宽 视DDR带宽而定
Shader Core Utilization 着色器核心利用率 30%-70%
注意: Streamline需要root权限或者eng版本的系统才能获取完整的硬件计数器。我曾经在user版本上折腾了半天,结果发现大部分计数器都是灰色的——白费功夫。

3.2.2 我的分析流程

拿到Streamline的数据后,我一般按这个顺序排查:

  1. 先看Shader Core Utilization——如果低于30%,说明GPU没吃饱,瓶颈在CPU或者带宽
  2. 再看External Bus Read——如果带宽打满了,那就是内存瓶颈,得考虑纹理压缩或者降低分辨率
  3. 最后看Fragment/Vertex Active——如果Fragment Active长期高于80%,说明片元着色器是瓶颈,得优化shader代码

我记得有一次,一个游戏场景在Mali-G52上跑不到30fps。Streamline一查,External Bus Read飙到了12GB/s,而MTK平台的DDR带宽上限也就15GB/s左右。说白了,就是纹理太大了,把带宽吃死了。后来把纹理从RGBA8888改成ETC2压缩,帧率直接翻倍。

3.3 MTK特有的GPU调优工具

MTK平台除了通用的Perfetto和Streamline,还有自己的一套工具。这些工具在座舱系统的实际调优中,往往能起到奇效。

3.3.1 MTK GPU DVFS调优

MTK的GPU有动态调频调压(DVFS)机制。但默认的DVFS策略有时候并不适合座舱场景——它更偏向省电,而不是保证流畅度。

查看当前GPU频率:

# 查看GPU当前频率
cat /proc/mali/frequency

# 查看支持的频率列表
cat /proc/mali/available_frequencies

强制锁定GPU频率(调试用):

# 锁定到最高频率
echo 800000 > /proc/mali/frequency_force

# 恢复自动调频
echo 0 > /proc/mali/frequency_force
关键点: 座舱系统的UI动画,我建议把GPU最低频率锁定在500MHz以上。默认的DVFS策略在低负载时会把GPU降到200多MHz,这时候来个突然的动画切换,就会有明显的卡顿感。

3.3.2 MTK GPU内存调试

MTK的GPU内存管理有个特点:它会把一部分系统内存当作「伪显存」来用。如果这部分内存分配不合理,就会导致性能抖动。

查看GPU内存使用:

# 查看GPU内存分配情况
cat /d/mali/memory

# 查看GPU页表使用
cat /d/mali/pages

我曾经在调一个双屏异显的座舱项目时,发现副屏的导航地图总是卡顿。用MTK的工具一看,发现GPU给副屏分配的显存只有8MB,而导航地图的纹理缓存就需要12MB。结果就是频繁的换入换出,性能自然好不了。

解决方案是在系统属性里调大GPU的预分配内存:

# 在init.rc中增加
setprop ro.mtk.gpu.memory.reserve 64

3.3.3 MTK GPU性能计数器

MTK在/sys/class/misc/mali0/device/下暴露了很多硬件计数器,可以直接用cat命令读取:

# 查看GPU利用率
cat /sys/class/misc/mali0/device/utilization

# 查看GPU周期计数
cat /sys/class/misc/mali0/device/cycle_counter
实用技巧: 我写了个简单的shell脚本,每秒采样一次这些计数器,然后输出到log里。这样在实车测试时,即使没有连接PC,也能事后分析GPU的行为。

3.4 三款工具的配合使用

这三款工具不是互相替代的关系,而是互补的。我一般的工作流是这样的:

  1. 先用Perfetto做全局扫描——看看是CPU瓶颈、GPU瓶颈还是I/O瓶颈
  2. 再用Streamline深入GPU内部——定位具体的shader或者带宽问题
  3. 最后用MTK工具做平台级调优——调整DVFS策略、内存分配等

举个例子,有一次座舱的仪表盘动画掉帧。Perfetto一看,GPU每帧渲染时间在18-20ms,明显超标。Streamline进一步分析,发现Fragment Active高达90%,说明片元着色器太重。最后用MTK工具把GPU频率锁定到最高,同时优化了shader里的alpha测试逻辑,终于把渲染时间压到了12ms以内。

嗯,这就是工具链的威力。没有这些工具,你就像在黑暗中摸索;有了它们,你就能精准地找到问题所在,然后一击必杀。