26、性能剖析工具:Systrace/Perfetto使用、GPU Profiler、解码器内部统计、自定义打点

做视频硬解码优化,最怕什么?

怕的是「感觉卡,但不知道卡在哪」。

我早年调试一个4K播放器,画面每隔几秒就抖一下。肉眼能看出来,但log里全是正常的。折腾了两天,最后用Systrace一抓——原来是GPU渲染管线里有个纹理上传操作卡了主线程。嗯,从那以后,我再也不敢凭感觉调优了。

这一章,我就把压箱底的性能剖析工具链拿出来。咱们一个一个说清楚。

26.1 Systrace与Perfetto:系统级追踪的「老大哥」和「新王者」

先说Systrace。这是Android早期就有的工具,基于ftrace。它能抓取CPU调度、SurfaceFlinger、VSync、Binder调用等系统事件。

我个人习惯,在分析解码卡顿时,先开Systrace看两个东西:

  • VSync信号是否稳定——如果VSync出现断层,说明渲染管线被阻塞了
  • 解码线程的CPU调度情况——看它是不是被频繁迁移到小核上

但Systrace有个硬伤:它只能抓几十秒,而且数据量一大就卡。所以Android 10之后,Google主推Perfetto。

Perfetto比Systrace强在哪?我列个表:

对比项SystracePerfetto
抓取时长通常5~10秒可长达数分钟
数据量超过100MB就卡死支持GB级数据
SQL分析不支持支持SQL查询
自定义数据源有限支持protobuf扩展
UI界面老旧Web UI全新UI,交互流畅

说白了,如果你还在用Systrace,建议尽快迁移到Perfetto。我去年把一个老项目从Systrace切到Perfetto,排查问题的效率至少提升了50%。

小技巧: Perfetto的SQL查询功能非常强大。比如你想找出所有耗时超过16ms的解码帧,直接写一句SQL就能搞定,不用肉眼在时间轴上慢慢找。

26.2 GPU Profiler:解码器之外的「隐形瓶颈」

很多人以为解码优化只看CPU就够了。其实不然。

你想想看,解码器输出的是一帧帧YUV或RGBA数据。这些数据最终要交给GPU去合成、显示。如果GPU这边处理不过来,照样掉帧。

Android上的GPU Profiler工具,我常用这几个:

  • AGI (Android GPU Inspector)——Google官方工具,支持抓取GPU指令、着色器、纹理内存
  • Snapdragon Profiler——高通平台专用,能看Adreno GPU的详细状态
  • Mali Graphics Debugger——ARM平台专用,适合Exynos/麒麟芯片

我曾经遇到一个案例:解码器输出帧率是60fps,但屏幕上只有30fps。用AGI一查,发现是GPU在合成时,每帧都要做一次色彩空间转换(YUV→RGB),这个操作占用了大量着色器周期。后来我们把转换逻辑挪到解码器的输出端,用CPU提前转好,GPU这边就轻松了。

注意: GPU Profiler需要设备开启开发者选项中的「GPU调试层」。部分厂商的ROM会限制这个功能,建议用原生Android设备或Google Pixel系列。

26.3 解码器内部统计:MediaCodec自带的「体检报告」

MediaCodec其实提供了不少内部统计信息,只是很多人不知道。

通过 MediaCodec.getOutputFormat()MediaCodec.getInputFormat(),你可以拿到一些关键指标:

// 获取解码器内部统计
MediaFormat format = codec.getOutputFormat();
int width = format.getInteger(MediaFormat.KEY_WIDTH);
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
// 注意:以下字段并非所有设备都支持
if (format.containsKey("encoder-delay")) {
    int delay = format.getInteger("encoder-delay");
    Log.d(TAG, "编码器延迟: " + delay + "帧");
}
if (format.containsKey("input-buffer-count")) {
    int inputCount = format.getInteger("input-buffer-count");
    Log.d(TAG, "输入缓冲区数量: " + inputCount);
}

但说实话,MediaCodec暴露的统计信息非常有限。更靠谱的方式,是使用 Codec2C2Param 接口。Codec2是Android 12之后主推的新一代编解码框架,它提供了更细粒度的控制参数。

举个例子,你可以通过 C2StreamPictureSizeInfo 拿到实际解码分辨率,通过 C2StreamAvgBitrateInfo 拿到码率统计。这些在MediaCodec的老接口里是拿不到的。

核心观点: 解码器内部统计是定位「硬件解码器本身是否异常」的最直接手段。如果解码器内部统计显示帧处理时间正常,但应用层掉帧,那问题一定出在解码器之外——比如渲染、传输、或CPU调度。

26.4 自定义打点:把「黑盒」变成「白盒」

工具再强,也有覆盖不到的地方。比如:你的业务逻辑里,从网络接收数据包到送入解码器,中间经过了几个队列?每个队列的等待时间是多少?

这些信息,Systrace和Perfetto都抓不到。怎么办?自己打点。

Android上自定义打点,我推荐两种方式:

  1. Trace.beginSection() / endSection()——最简单,直接写入系统trace,可以在Systrace/Perfetto里看到
  2. 自定义环形缓冲区——适合高频打点场景,避免log过多影响性能

下面是我在实际项目中用过的打点模板:

public class DecoderTrace {
    private static final String TAG = "DecoderTrace";
    private static final int BUFFER_SIZE = 1024;
    private final long[] timestamps = new long[BUFFER_SIZE];
    private final String[] labels = new String[BUFFER_SIZE];
    private int index = 0;

    public void mark(String label) {
        // 写入系统trace
        Trace.beginSection(label);
        Trace.endSection();
        // 同时写入环形缓冲区
        timestamps[index] = System.nanoTime();
        labels[index] = label;
        index = (index + 1) % BUFFER_SIZE;
    }

    public void dump() {
        // 输出最近BUFFER_SIZE条记录
        for (int i = 0; i < BUFFER_SIZE; i++) {
            if (labels[i] != null) {
                Log.d(TAG, labels[i] + " @ " + timestamps[i]);
            }
        }
    }
}

使用的时候,在关键路径上打点:

trace.mark("网络接收完成");
// ... 数据入队 ...
trace.mark("数据入队");
// ... 解码器dequeueInputBuffer ...
trace.mark("送入解码器");
// ... 解码器返回输出 ...
trace.mark("解码完成");

我曾经用这套方案,定位到一个诡异的「间歇性卡顿」。Perfetto上看CPU和GPU都正常,但自定义打点显示:每隔30秒左右,网络接收完成到数据入队之间,会多出200ms的延迟。最后发现是网络库的GC引起的。嗯,这种问题,没有自定义打点根本查不出来。

建议: 自定义打点的开销要控制在微秒级。不要在里面做字符串拼接、log输出等耗时操作。环形缓冲区+纳秒时间戳是最轻量的方案。

26.5 知识体系总览

说了这么多,我把这四种工具的关系画成一张图,方便你理解:

Android视频硬解码性能剖析工具体系 系统级追踪 Systrace / Perfetto CPU调度、VSync、Binder、SurfaceFlinger Perfetto支持SQL分析,推荐优先使用 GPU Profiler AGI / Snapdragon / Mali 着色器、纹理内存、渲染管线 定位合成阶段的隐形瓶颈 解码器内部统计 MediaCodec / Codec2 C2Param 帧处理时间、缓冲区状态、码率 判断解码器本身是否异常 自定义打点 Trace.beginSection / 环形缓冲区 覆盖工具无法触及的业务逻辑 定位队列等待、GC等隐蔽问题 相互补充,缺一不可 从系统层 → GPU层 → 解码器层 → 业务层,逐层穿透

这四种工具,不是互相替代的关系,而是互补的。系统级追踪看大方向,GPU Profiler查渲染瓶颈,解码器内部统计确认硬件状态,自定义打点补全业务细节。四者结合,才能把解码性能问题彻底挖干净。

好了,这一章的内容就到这里。下一章我们聊聊具体怎么用Perfetto抓取解码器的trace数据,以及如何解读那些花花绿绿的时间轴。


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