第23章 性能分析与优化:Systrace使用指南、Perfetto性能分析、CPU/GPU频率调节、内存泄漏检测
性能优化这事儿,说难也难,说简单也简单。我做了这么多年嵌入式,最大的体会就是——你得先会看数据,再谈优化。没有数据支撑的优化,都是瞎蒙。
这一章,咱们就聊聊Android Automotive系统里最常用的几个性能分析工具。Systrace、Perfetto、CPU/GPU调频、内存泄漏检测,这四个东西你玩明白了,系统卡顿、掉帧、发热这些问题,基本都能找到根因。
23.1 Systrace使用指南
Systrace是Google官方提供的性能追踪工具。说白了,它能把系统里各个进程在干什么、花了多长时间,用时间轴的方式展示出来。你想想看,这比靠猜靠谱多了。
我在项目中遇到过好几次,车机启动慢,UI掉帧,大家都说是驱动问题。结果一抓Systrace,发现是某个Service在启动时做了大量IO操作,把主线程堵死了。嗯,工具不会骗人。
23.1.1 基本用法
抓取Systrace的方式很简单,命令行搞定:
# 抓取5秒的trace,包含所有标签
python systrace.py -t 5 -o trace.html
# 抓取特定标签,减少数据量
python systrace.py -t 5 -o trace.html gfx input view webview
我个人习惯在抓取时加上 -a 包名 参数,这样能追踪到应用层的事件:
python systrace.py -t 5 -o trace.html -a com.android.car
23.1.2 关键指标怎么看
Systrace打开后,你会看到一堆彩色的条条。别慌,重点看这几个:
- SurfaceFlinger:负责合成显示,如果这里出现大段的空白,说明GPU在等数据
- Choreographer:负责帧率控制,掉帧的话这里会有红色标记
- CPU频率:看CPU是不是在偷懒,频率上不去会导致任务堆积
- Binder事务:跨进程调用,如果Binder调用耗时太长,说明服务端响应慢
我曾经遇到一个案例,车机滑动列表卡顿。看Systrace发现SurfaceFlinger每帧都在等一个Binder调用返回。查下去,是某个ContentProvider查询数据库太慢。优化SQL索引后,问题解决。
23.1.3 自定义Trace点
有时候系统自带的Trace点不够用,你可以在自己的代码里加:
// Java层
Trace.beginSection("my_custom_trace");
// 你的代码
Trace.endSection();
// Native层
ATRACE_BEGIN("my_native_trace");
// 你的代码
ATRACE_END();
23.2 Perfetto性能分析
Perfetto是Systrace的升级版。说实话,Systrace在Android 10之后基本被Perfetto取代了。Perfetto能抓的数据更多,分析能力更强,而且支持SQL查询。
23.2.1 抓取Perfetto数据
Perfetto有两种抓取方式:命令行和网页工具。我推荐用命令行,可控性更强:
# 抓取10秒数据
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq idle am wm gfx view
# 拉取到本地
adb pull /data/misc/perfetto-traces/trace.perfetto-trace .
抓完后,打开 https://ui.perfetto.dev,把trace文件拖进去就能看了。
23.2.2 Perfetto的核心优势
| 特性 | Systrace | Perfetto |
|---|---|---|
| 数据量 | 有限,容易丢数据 | 支持长时间抓取,数据完整 |
| SQL查询 | 不支持 | 支持,可以精确筛选 |
| 多进程分析 | 基本支持 | 支持更细粒度 |
| 内存分析 | 不支持 | 支持Native内存追踪 |
我个人特别喜欢Perfetto的SQL查询功能。比如我想看所有耗时超过10ms的Binder调用:
SELECT name, dur FROM slice
WHERE name GLOB '*binder*' AND dur > 10000000
ORDER BY dur DESC
这比肉眼在时间轴上找快多了。
23.2.3 实战:分析启动卡顿
我记得有一次,客户反馈车机冷启动时,中控屏要等3秒才亮。用Perfetto抓了启动trace,发现SurfaceFlinger在等待一个叫 car_service_init 的Binder调用。查代码,原来是CarService在启动时同步加载了所有车载设备的配置,里面有个设备响应超时,等了2秒。
优化方案很简单:把配置加载改成异步,先让UI显示出来,后台慢慢加载。启动时间从3秒降到了0.8秒。
23.3 CPU/GPU频率调节
CPU和GPU的频率调节,直接影响系统性能和功耗。调高了,性能好但发热;调低了,省电但卡顿。怎么找到平衡点?
23.3.1 CPU调频策略
Android系统默认使用 interactive 或 schedutil 调频器。你可以通过 /sys/devices/system/cpu/cpu*/cpufreq/ 下的节点查看和修改:
# 查看当前调频策略
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 修改为performance模式(最高性能)
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 查看可用频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
在车载场景下,我建议不要用 performance 模式,发热太严重。用 schedutil 配合自定义的调频阈值,效果更好。
23.3.2 GPU调频
GPU调频和CPU类似,节点在 /sys/class/kgsl/kgsl-3d0/ 下:
# 查看GPU当前频率
cat /sys/class/kgsl/kgsl-3d0/gpuclk
# 设置最大频率
echo 500000000 > /sys/class/kgsl/kgsl-3d0/max_gpuclk
我曾经在项目里遇到一个问题:车机导航时,地图滑动卡顿。看Perfetto发现GPU频率一直上不去,只有200MHz。查了调频策略,发现温度传感器误报高温,导致GPU被限频。修复温度传感器后,GPU能跑到600MHz,卡顿消失。
23.4 内存泄漏检测
内存泄漏是Android系统的老大难问题。尤其是车载系统,7x24小时运行,内存泄漏会导致系统越来越慢,最后OOM重启。
23.4.1 使用LeakCanary
LeakCanary是Square公司开源的Android内存泄漏检测库。集成简单,效果显著:
// build.gradle
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
}
// 自动检测,无需额外代码
// 当发生内存泄漏时,会弹出通知并生成dump文件
LeakCanary适合开发阶段使用。但在车载系统上,我建议只在debug版本开启,release版本一定要关掉,否则会影响性能。
23.4.2 使用Android Studio Profiler
Android Studio自带的Profiler工具,可以实时查看内存使用情况:
- 连接设备,打开Android Studio
- 点击右下角的 Profiler 标签
- 选择你的进程,点击 Memory
- 录制一段操作,然后点击 Dump Java heap
- 分析dump文件,查看哪些对象没有被释放
我习惯在Dump heap后,用 Analyzer Tasks 自动检测泄漏。它会列出所有可能的泄漏路径,省去手动分析的麻烦。
23.4.3 Native内存泄漏检测
Java层的内存泄漏相对好查,Native层就麻烦多了。我推荐使用 AddressSanitizer (ASan) 或 HWASan:
# 在Android.mk中启用ASan
LOCAL_SANITIZE := address
LOCAL_SANITIZE_DIAG := address
# 或者在Android.bp中
sanitize: {
address: true,
diag: {
address: true,
},
}
启用ASan后,如果有内存越界或泄漏,程序会直接crash并打印详细的调用栈。虽然会降低运行速度,但定位问题非常精准。
23.5 本章知识体系
下面这张图,把本章的核心内容串起来了。你可以看到,性能分析是一个闭环:先发现问题,再定位根因,然后优化,最后验证。
这张图把四个工具串起来了。Systrace和Perfetto负责发现问题,CPU/GPU调频负责性能调节,内存泄漏检测负责稳定性。三者配合,才能打造一个流畅稳定的车载系统。
好了,这一章的内容就到这里。工具和方法都给你了,剩下的就是多练。性能优化没有捷径,唯手熟尔。
公众号:蓝海资料掘金营,微信deep3321