性能基准建立: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
我的经验:systrace 的 -a 参数可以指定要跟踪的 app 包名。不加的话,很多 app 内部的自定义 trace 点看不到。

抓出来的 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 的配置文件是 YAML 格式,缩进不能错。我曾经因为一个空格没对齐,抓了半天发现数据源没启动——白忙活。

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% 的帧在多少毫秒内完成。这个比平均帧率更能反映真实体验。
核心原则:平均帧率 60 不代表不卡。P99 帧时间超过 50ms,用户就能感知到掉帧。我一般把 P99 ≤ 30ms 作为「流畅」的硬门槛。

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 采集前的环境准备

  1. 关闭后台干扰:飞行模式、关闭通知、关闭自动同步。我吃过一次亏——没关微信,结果 trace 里全是网络请求,根本看不清 HAL 的行为。
  2. 固定设备状态:屏幕亮度固定 50%、连接同一 Wi-Fi(或关闭 Wi-Fi)、充电状态一致。
  3. 预热设备:跑 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"
我的习惯:每次采集至少重复 3 次,取中位数。单次数据可能有偶然波动,3 次以上才有统计意义。

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 定义 帧率类:FPS / Jank / P99 启动类:冷启动 / 热启动 / 触控延迟 资源类:CPU / GPU / 内存 / 功耗 基线采集 环境准备 → 自动化脚本 → 重复 3 次 元信息记录 → Git 版本管理 可对比的基线数据集 核心思想:先测量,再优化。没有基线数据,一切调优都是盲人摸象。

本章要点回顾:

  • systrace 适合快速排查,perfetto 适合深度分析——两个都要会。
  • KPI 要具体、可量化。别只说「卡」,要说「P99 帧时间超过 30ms」。
  • 基线采集要自动化、重复多次、记录元信息。用 Git 管理基线数据,方便回溯对比。