性能基准建立:perfetto与systrace基础、关键性能指标(KPI)定义、基线数据采集方法
性能调优这件事,说白了就是「先测量,再优化」。没有基线数据,你连问题在哪都不知道。我见过太多团队一上来就改代码、调参数,结果越调越糟——因为根本不知道改之前是什么水平。
这一章,我们就来搞定三件事:用什么工具看、看什么指标、怎么把基线数据存下来。
2.1 性能分析工具:perfetto 与 systrace
Android 性能分析工具经历了一次换代。老牌工具 systrace 在 Android 10 之后逐步被 perfetto 取代。但说实话,systrace 在低版本设备上仍然有用,两个都得会。
2.1.1 systrace 基础用法
systrace 基于 ftrace,抓取内核和用户态的 trace 事件。我个人习惯把它当作「快速看一眼」的工具——启动快、开销低。
# 抓取 10 秒,包含常见标签
python systrace.py -t 10 -o trace.html sched freq gfx view input am wm
抓出来的 HTML 文件用 Chrome 打开。按 W 放大、S 缩小、M 标记区间。嗯,快捷键记一下,后面天天用。
2.1.2 perfetto 核心能力
perfetto 是 systrace 的全面升级版。它支持更长的抓取时间、更大的缓冲区、更细粒度的配置。我在项目中遇到过一个诡异问题——systrace 抓 5 秒根本复现不了,换成 perfetto 抓 30 秒才抓到那个一闪而过的卡顿。
# 使用 perfetto 命令行抓取
adb shell perfetto \
-c - --txt \
-o /data/misc/perfetto-traces/trace.perfetto-trace \
<<< 'duration_ms: 15000
buffers:
- size_kb: 65536
fill_policy: RING_BUFFER
data_sources:
- config: { name: "linux.ftrace" }
- config: { name: "linux.process_stats" }
- config: { name: "android.surfaceflinger.frametimeline" }'
perfetto 的 UI 界面(ui.perfetto.dev)比 systrace 强太多。你可以直接搜索事件、计算区间耗时、导出 CSV。我个人最常用的是 SQL 查询模式——直接写 SQL 分析 trace 数据,比肉眼快十倍。
2.1.3 两个工具怎么选?
| 对比维度 | systrace | perfetto |
|---|---|---|
| 抓取时长 | 通常 ≤ 10 秒 | 可到数分钟 |
| 缓冲区大小 | 固定较小 | 可配置,最大 256MB+ |
| 数据源扩展 | 有限 | 插件化,可自定义 |
| 分析能力 | 可视化浏览 | 可视化 + SQL 查询 |
| 适用场景 | 快速排查、低版本设备 | 深度分析、长时间监控 |
我的建议是:日常快速看用 systrace,深入分析用 perfetto。两个都装好,不冲突。
2.2 关键性能指标(KPI)定义
工具会用了,接下来得知道「看什么」。性能指标不能拍脑袋定,得跟业务场景挂钩。我参与过的项目中,最常踩的坑就是「指标定义不清晰」——你说卡顿,我说流畅,最后吵半天发现标准不一样。
2.2.1 帧率相关指标
- FPS(Frames Per Second):每秒渲染帧数。稳定 60 是及格线,90 或 120 是高端线。
- Jank(卡顿次数):单帧渲染时间超过 16.67ms(60Hz)的次数。注意,不是掉帧就叫 jank,得看是否超过 vsync 周期。
- Frame Time P99/P95:99% 或 95% 的帧在多少毫秒内完成。这个比平均帧率更能反映真实体验。
2.2.2 启动与响应指标
- 冷启动时间:从点击图标到首帧显示。我习惯用
adb logcat -s ActivityManager抓取 Displayed 时间。 - 热启动时间:从后台切回前台到可交互。这个指标对 HAL 层影响较小,但上层经常拿它说事。
- 触控响应延迟:从触摸事件到屏幕更新的总延迟。HAL 层的 input 处理、显示合成都会影响这个值。
2.2.3 资源消耗指标
| 指标 | 说明 | HAL 层关注点 |
|---|---|---|
| CPU 使用率 | 各核心负载百分比 | 驱动线程、中断处理是否异常 |
| GPU 使用率 | 渲染单元占用 | HAL 提交的 buffer 是否过多 |
| 内存占用 | RSS / PSS | HAL 服务是否有内存泄漏 |
| 功耗 | mA 或 mW | HAL 是否频繁唤醒系统 |
2.3 基线数据采集方法
指标定义好了,接下来就是「怎么采」。基线数据不是随便跑一次就行的,得有方法、有流程。
2.3.1 采集前的环境准备
- 关闭后台干扰:飞行模式、关闭通知、关闭自动同步。我吃过一次亏——没关微信,结果 trace 里全是网络请求,根本看不清 HAL 的行为。
- 固定设备状态:屏幕亮度固定 50%、连接同一 Wi-Fi(或关闭 Wi-Fi)、充电状态一致。
- 预热设备:跑 3-5 分钟相同场景,让 CPU/GPU 频率稳定下来。冷机跑出来的数据没有参考价值。
2.3.2 采集流程
我一般用脚本自动化采集,避免手动操作带来的误差。下面是一个简单的 perfetto 采集脚本框架:
#!/bin/bash
# 基线采集脚本示例
DEVICE=$1
DURATION=$2
OUTPUT_DIR="baseline_$(date +%Y%m%d_%H%M%S)"
mkdir -p $OUTPUT_DIR
# 1. 清除 logcat 缓存
adb -s $DEVICE logcat -c
# 2. 启动 perfetto 抓取
adb -s $DEVICE shell perfetto \
-c /data/local/tmp/perfetto_config.pbtxt \
-o /data/misc/perfetto-traces/trace.perfetto-trace &
PERFETTO_PID=$!
# 3. 执行测试场景(例如滑动列表)
adb -s $DEVICE shell am start -W com.example.app/.MainActivity
sleep 2
# 这里可以插入自动化测试命令
# 4. 等待抓取完成
sleep $DURATION
adb -s $DEVICE shell kill $PERFETTO_PID
# 5. 拉取 trace 文件
adb -s $DEVICE pull /data/misc/perfetto-traces/trace.perfetto-trace $OUTPUT_DIR/
echo "基线数据已保存到 $OUTPUT_DIR"
2.3.3 数据记录与版本管理
基线数据不能只存 trace 文件,还得记录元信息。我建议用表格记录每次采集的上下文:
| 字段 | 示例值 | 说明 |
|---|---|---|
| 设备型号 | Pixel 7 Pro | 硬件平台 |
| Android 版本 | 14 (UP1A.231005.007) | 系统版本 + 构建号 |
| HAL 版本 | v2.1 | HAL 接口版本 |
| 测试场景 | Launcher 滑动 | 具体操作描述 |
| 采集时间 | 2024-03-15 14:30 | 精确到分钟 |
| 平均 FPS | 58.3 | 从 trace 中提取 |
| Jank 次数 | 12 | 超过 16.67ms 的帧数 |
| P99 帧时间 | 28.5ms | 99% 分位帧耗时 |
这些数据建议用 Git 管理,每次改完 HAL 代码后重新采集,对比差异。我曾经靠这个表格发现了一个 camera HAL 的内存泄漏——基线数据里内存占用是 120MB,某次改版后涨到 180MB,一查果然有 buffer 没释放。
2.4 本章知识体系
下面这张图总结了性能基准建立的完整流程:
本章要点回顾:
- systrace 适合快速排查,perfetto 适合深度分析——两个都要会。
- KPI 要具体、可量化。别只说「卡」,要说「P99 帧时间超过 30ms」。
- 基线采集要自动化、重复多次、记录元信息。用 Git 管理基线数据,方便回溯对比。