26. 启动优化工具链:Perfetto深度使用、SimplePerf、Firebase Performance

做启动优化,最怕什么?

怕你改了一堆代码,自我感觉良好,结果一测——更慢了。

所以,工具链就是你的眼睛。没有它,你就是在黑灯瞎火里修车。今天我把压箱底的三件套拿出来:Perfetto、SimplePerf、Firebase Performance。这三样东西,我几乎每天都要跟它们打交道。

Perfetto:启动优化的“显微镜”

Perfetto 是 Google 官方的性能追踪工具。说白了,它能把你手机从开机到应用启动完成,每一毫秒在干什么都记录下来。

我个人习惯:先跑一次 Perfetto,再看火焰图。为什么?因为 Perfetto 能告诉你“时间花在哪了”,而火焰图告诉你“CPU 在忙什么”。两者缺一不可。

Perfetto 的三种启动模式

模式 命令 适用场景
轻量模式 perfetto --txt -c /path/to/config -o /data/misc/perfetto-traces/trace.perfetto-trace 快速定位主线程卡顿
详细模式 perfetto --txt -c /path/to/config -o /data/misc/perfetto-traces/trace.perfetto-trace --full 分析 Binder 调用、锁竞争
持续追踪 通过 Perfetto UI 的“Record new trace”按钮 复现偶现问题

嗯,这里要注意:轻量模式就够了,别一上来就开全量。我见过有人开全量追踪,结果手机直接卡死,重启后 trace 文件还损坏了。

我常用的 Perfetto 配置

buffers: {
    size_kb: 522240
    fill_policy: DISCARD
}
data_sources: {
    config {
        name: "android.surfaceflinger.frametimeline"
    }
}
data_sources: {
    config {
        name: "android.scheduling"
    }
}
data_sources: {
    config {
        name: "linux.syscalls"
    }
}
duration_ms: 10000

这个配置只抓 10 秒,重点看 SurfaceFlinger 帧时间线和系统调用。我曾经用这个配置抓到一个诡异问题:启动时有个线程在疯狂做 futex 系统调用,导致主线程被频繁挂起。查了三天,最后发现是某个第三方 SDK 在后台轮询。

SimplePerf:CPU 火焰图的利器

Perfetto 告诉你“时间线”,SimplePerf 告诉你“CPU 在跑什么代码”。

为什么需要它?因为有时候启动慢,不是主线程卡,而是某个后台线程把 CPU 吃满了。你想想看,8 核 CPU,如果 4 个核都在跑垃圾回收,主线程能快吗?

基本用法

# 采样 10 秒,生成火焰图数据
simpleperf record -p $(pidof com.example.app) -e cpu-clock -f 1000 --duration 10 -o perf.data

# 生成报告
simpleperf report -i perf.data --sort comm,dso,symbol -o report.txt

# 生成火焰图
simpleperf report_html -i perf.data -o flamegraph.html

避坑指南:我曾经在某个项目上直接用 simpleperf record 不加 -f 参数,结果采样频率太低,火焰图里全是“unknown”。后来才意识到,默认采样频率只有 100Hz,对于启动这种毫秒级事件,根本不够看。建议至少 1000Hz。

火焰图怎么看?

记住三个原则:

  • 看宽度:哪个函数占的 CPU 时间最长,哪个就是嫌疑犯
  • 看颜色:红色通常表示内核态,蓝色表示用户态
  • 看堆叠:如果某个调用链特别深,说明可能有过度封装

我见过最离谱的一次:某个应用的启动火焰图里,android.os.Handler.dispatchMessage 占了 40% 的 CPU 时间。点进去一看,好家伙,一个 Handler 里塞了 200 多个 Runnable,每个都做了一点点 IO 操作。这就是典型的“微任务堆积”。

Firebase Performance:线上监控的“雷达”

Perfetto 和 SimplePerf 都是本地工具。但线上用户的环境千奇百怪,你不可能让每个用户都连 ADB 抓 trace。

Firebase Performance 就是干这个的。它能自动采集启动时间、网络请求、屏幕渲染等指标,然后上报到后台。

核心指标

指标 含义 我建议的阈值
App Start Time 从点击图标到首帧渲染 < 1.5s (P50), < 3s (P95)
Cold Start 冷启动时间 < 2s
Warm Start 热启动时间 < 1s
HTTP Response Time 网络请求响应时间 < 200ms

自定义追踪

Firebase Performance 还支持自定义 Trace。我通常会在关键路径上加:

Trace trace = FirebasePerformance.getInstance().newTrace("cold_start_init");
trace.start();

// 初始化各个模块
initSDK();
initDatabase();
initUI();

trace.stop();

这样你就能看到每个模块花了多少时间。我记得有一次,线上数据显示 initSDK() 在 P95 上花了 800ms,而本地测试只有 200ms。后来发现是某个 SDK 在弱网环境下会做超时重试,导致启动变慢。

核心观点:本地工具(Perfetto + SimplePerf)解决“能不能优化”的问题,线上工具(Firebase Performance)解决“优化有没有效果”的问题。两者缺一不可。

三件套的配合使用

我一般的工作流是这样的:

  1. 线上报警:Firebase Performance 发现启动时间异常
  2. 本地复现:用 Perfetto 抓 trace,看时间线
  3. 定位热点:用 SimplePerf 生成火焰图,找到 CPU 热点
  4. 优化代码:改完后,再用 Perfetto 验证
  5. 上线观察:Firebase Performance 确认指标下降

这个闭环,我用了好几年,屡试不爽。

小技巧:Perfetto 的 trace 文件通常很大(几百 MB),建议用 --compress 参数压缩后再分享。我一般会压缩成 .gz 格式,能缩小 10 倍以上。

警告:不要在用户手机上开启 Perfetto 或 SimplePerf 的持续追踪。这两个工具会显著增加功耗和 CPU 负载。线上监控请只用 Firebase Performance 这类轻量级方案。

知识体系总览

下面这张图,是我对启动优化工具链的理解。你可以把它当作一个“作战地图”:

启动优化工具链知识体系 Perfetto SimplePerf Firebase Performance 时间线分析 • 主线程卡顿 • Binder 调用 • 帧时间线 CPU 热点分析 • 火焰图 • 函数调用链 • 采样统计 线上监控 • 启动时间 • 网络请求 • 自定义 Trace 工作流:线上报警 → 本地复现 → 定位热点 → 优化验证 → 上线观察 本地工具解决“能不能优化”,线上工具解决“优化有没有效果” 本地工具 线上工具

这张图里,Perfetto 和 SimplePerf 是本地工具,Firebase Performance 是线上工具。它们不是替代关系,而是互补关系。你想想看,没有本地工具,你连问题在哪都不知道;没有线上工具,你改了代码也不知道有没有效果。

好了,工具链就讲到这里。下一章我们聊聊具体怎么用这些工具去优化启动流程——从 Application 的 onCreate 开始,一步步拆解。


公众号:蓝海资料掘金营,微信deep3321