26、性能优化与Systrace:Trace原理、Perfetto、CPU/GPU/IO瓶颈分析

性能优化,说白了就是跟时间赛跑。你写的代码再漂亮,跑起来卡顿,用户照样骂娘。我在Android底层摸爬滚打这些年,最深的体会就是——没有Systrace和Perfetto,你根本不知道你的系统在干什么。

今天我们就来聊聊这套工具链。从Trace原理讲起,再到Perfetto这个新一代神器,最后手把手教你分析CPU、GPU、IO三大瓶颈。

Trace原理:系统到底在记录什么?

先问个问题:Trace到底是什么?

简单说,Trace就是系统在特定时间点打下的“时间戳+事件名”标记。Android的Trace机制,核心就是atraceftrace

ftrace是Linux内核自带的跟踪器。它能记录函数调用、调度切换、中断等内核事件。Android在它上面封装了一层,叫atrace,用来统一管理用户态和内核态的Trace数据。

我举个例子。你在App里调用:

Trace.beginSection("bindView");
// 你的业务逻辑
Trace.endSection();

这背后发生了什么?

  • 应用层通过Binder通知atrace守护进程
  • atraceftrace缓冲区写入一条记录
  • 记录包含:时间戳、进程ID、线程ID、事件名称
  • 最终被Systrace或Perfetto读取并可视化

核心要点:Trace的开销极低,微秒级别。所以线上环境也可以开启部分Trace,不会明显影响性能。我曾在某款千万级DAU的App里,长期开着关键路径的Trace,完全没问题。

Perfetto:Systrace的全面进化

说实话,Systrace虽然经典,但已经有点老了。Google自己也意识到了这点,所以推出了Perfetto。

Perfetto跟Systrace比,强在哪?

对比项 Systrace Perfetto
数据源 仅ftrace + atrace ftrace + atrace + heapprofd + traced_perf + 自定义数据源
文件格式 HTML(自包含) Protobuf(二进制,更小更快)
分析能力 基本的时间轴查看 SQL查询、自定义指标、火焰图、内存分析
适用场景 简单问题快速定位 复杂性能问题、内存泄漏、长时间跟踪

我个人习惯是:日常快速排查用Systrace,深入分析用Perfetto。尤其是Perfetto的SQL查询功能,简直神器。你可以直接写SQL查某个时间段内所有GC事件的耗时,比肉眼扫图快一百倍。

小技巧:Perfetto的UI界面(ui.perfetto.dev)支持直接拖拽Trace文件分析。你甚至可以在线分享Trace文件给同事,不用再发大文件了。

CPU瓶颈分析:谁在抢时间片?

CPU瓶颈,说白了就是“活太多,核不够用”。或者“活不多,但调度不合理”。

看Systrace/Perfetto的CPU调度区,我一般关注这几个点:

  • CPU利用率曲线:如果某个核长期100%,说明有线程在死循环或密集计算
  • 线程状态颜色:绿色(运行中)、蓝色(可运行但没拿到时间片)、灰色(休眠)
  • 调度延迟:一个线程从“可运行”到“真正运行”的间隔,超过5ms就要警惕

我曾经遇到过一个案例:某款手机刷抖音时发热严重。打开Perfetto一看,发现有个后台服务线程在不停地轮询传感器数据,占满了CPU小核。解决方案很简单——加个休眠策略,问题就解决了。

注意:不要只看CPU总利用率。有时候总利用率只有30%,但某个关键线程(比如UI线程)被频繁抢占,照样卡顿。要盯着具体线程看。

GPU瓶颈分析:渲染管线的堵点

GPU瓶颈比CPU更隐蔽。因为GPU是“黑盒”,你很难直接看到它在干什么。

不过Perfetto给了我们一个利器——gpu.renderstagegpu.memory这两个数据源。

看GPU瓶颈,我一般分三步:

  1. 看帧率曲线:如果掉帧,先看是CPU侧提交慢,还是GPU侧渲染慢
  2. 看渲染阶段:Perfetto会显示每个帧的“提交-顶点处理-像素处理-完成”各阶段耗时
  3. 看GPU频率:如果GPU频率跑满但帧率还是低,说明渲染负载确实重;如果频率很低但帧率低,说明驱动或CPU侧有瓶颈

嗯,这里要注意。很多开发者一看到掉帧就怪GPU,其实大部分情况是CPU侧的UI线程被阻塞了。GPU只是“背锅侠”。

IO瓶颈分析:最容易被忽视的杀手

IO瓶颈,是我见过最多、也最容易被忽视的性能问题。

为什么?因为IO操作通常不会直接导致ANR,但会让App“一顿一顿”的。用户感觉就是“不跟手”。

在Perfetto里看IO,主要看这几个指标:

  • f2fs/ext4的IO延迟:单次读写超过100ms就算异常
  • IO等待队列长度:如果队列里排了十几个请求,说明存储真的忙不过来了
  • page cache命中率:命中率低,说明频繁读磁盘,缓存策略有问题

我记得有一次,某App启动时读取一个10MB的配置文件。每次启动都读,导致启动速度慢得像蜗牛。用Perfetto一看,IO延迟高达200ms。解决方案很简单——把配置文件改成mmap映射,只读需要的部分,启动速度直接提升40%。

避坑指南:我曾经在项目里遇到一个诡异问题——手机存储空间快满时,IO性能急剧下降。后来查资料才知道,f2fs文件系统在剩余空间低于10%时,GC(垃圾回收)频率会大幅增加,导致IO抖动。所以,如果你的App在低存储设备上表现差,先看看是不是IO问题。

实战:用Perfetto抓一次完整的性能分析

光说不练假把式。我带你走一遍完整的流程。

首先,开启Perfetto跟踪:

# 抓取10秒的Trace,包含CPU、GPU、IO、内存数据
adb shell perfetto \
  -c - --txt \
  -o /data/misc/perfetto-traces/trace.perfetto-trace \
  <<< 'duration_ms: 10000
  buffers: {
    size_kb: 65536
    fill_policy: RING_BUFFER
  }
  data_sources: {
    config {
      name: "linux.ftrace"
      ftrace_config {
        ftrace_events: "sched/sched_switch"
        ftrace_events: "power/cpu_frequency"
        ftrace_events: "f2fs/f2fs_read"
        ftrace_events: "f2fs/f2fs_write"
      }
    }
  }
  data_sources: {
    config {
      name: "gpu.memory"
    }
  }'

然后拉取文件:

adb pull /data/misc/perfetto-traces/trace.perfetto-trace .

最后拖到 ui.perfetto.dev 里分析。我习惯先看“CPU调度”和“IO延迟”这两个面板,基本能覆盖80%的问题。

SVG:性能分析核心逻辑图

下面这张图,是我自己总结的性能分析核心逻辑。你照着这个思路走,基本不会漏掉关键点。

Android性能分析核心逻辑 问题现象 卡顿 / 掉帧 / 发热 / 启动慢 工具选择 Systrace(快速) / Perfetto(深入) CPU瓶颈 调度延迟 线程抢占 GPU瓶颈 渲染阶段耗时 GPU频率不足 IO瓶颈 读写延迟高 缓存命中率低 解决方案 优化调度 / 减少渲染负载 / 缓存策略 / 异步IO

这张图的核心思想就是:先看现象,再选工具,然后定位三大瓶颈之一,最后对症下药。别一上来就瞎调参数,那是浪费时间。

总结

性能优化没有银弹。但有了Systrace和Perfetto这两把利器,你至少能看清问题在哪。CPU、GPU、IO,这三者往往是相互影响的。CPU慢了,GPU就得等;IO慢了,CPU就得等。所以分析时要全局看,别只盯着一个点。

我个人建议:每个Android开发者都应该学会看Perfetto的SQL查询。它能把你的分析效率提升一个数量级。嗯,今天就聊到这,剩下的靠你们自己去实践了。


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