用 simpleperf 找性能热点:不要凭感觉优化 native 代码
性能优化最怕“我感觉这里慢”。native 代码离 CPU 更近,也更容易让人误以为自己能凭经验判断热点。正确做法是先采样,再看证据。
simpleperf 是 Android/NDK 提供的 CPU profiling 工具。官方文档说明,它包含在 NDK 中,可以通过命令行分析 native 程序的 CPU 使用情况。
性能热点是什么
热点不是“看起来复杂的代码”,而是 CPU 真正在那里花了很多时间的代码。
比如播放器卡顿,你可能以为是解码慢,实际可能是:
memcpy 太多
日志太频繁
JNI 回调太密
锁竞争严重
颜色转换占用 CPU
渲染线程被 sleep 策略拖住
simpleperf 的作用就是把“猜测”变成“采样数据”。
采样的直觉
采样 profiler 像每隔一小段时间拍一张现场照片,看看 CPU 正在执行哪里。拍得多了,就能统计哪些函数出现频率最高。
函数 A 出现 500 次
函数 B 出现 80 次
函数 C 出现 10 次
函数 A 很可能是热点。
准备符号
native profiling 需要符号。建议用 RelWithDebInfo。
Release 太干净,符号可能不够
Debug 太慢,性能不接近真实
RelWithDebInfo 有优化,也保留调试信息
如果没有符号,报告只能看到地址或不完整函数名,分析价值会大幅下降。
基本采集流程
simpleperf record -p <pid> -g --duration 10
simpleperf report
参数含义:
record:采集性能数据
-p:指定进程
-g:采集调用栈
--duration:采集时长
report:查看报告
实际项目可用 NDK 中的 simpleperf 脚本和 Android Studio CPU Profiler 配合。
看报告时先看三个维度
第一,看线程。
main thread
demux thread
decode thread
render thread
audio thread
如果主线程 CPU 很高,可能是 JNI 或 UI 事件回调太重。
如果 render 线程 CPU 很高,可能是帧调度或后处理问题。
第二,看 DSO。
DSO 就是动态库。
libplayer_core.so
libmediandk.so
libc.so
libart.so
这能判断时间花在你自己的库、系统媒体库,还是 ART/JNI 边界。
第三,看函数。
PlayerRenderer::renderFrame
memcpy
ColorConverter::yuvToRgb
pthread_mutex_lock
函数层才是具体优化入口。
播放器热点示例
报告显示:
35% ColorConverter::yuvToRgb
18% memcpy
12% pthread_mutex_lock
推理:
颜色转换占比最高,可能需要 Neon 或 GPU 路径
memcpy 占比高,说明帧数据拷贝太多
mutex 占比高,说明队列锁竞争明显
这时优化顺序不是“随便改算法”,而是按占比和风险排序。
优化闭环
采样
提出假设
做最小改动
再次采样
比较数据
决定保留或回滚
例如:
假设:PacketQueue 锁竞争导致 decode 卡顿
改动:把大锁缩小,减少锁内 memcpy
复测:pthread_mutex_lock 从 12% 降到 3%
结论:保留
没有复测的优化,只是改代码。
指标不要只看平均值
播放器更关心尾部延迟。
平均帧耗时
P95 帧耗时
P99 帧耗时
掉帧数
首帧时间
seek 后恢复时间
平均值好看不代表体验好。用户能感受到的是偶发大卡顿。
本章实验
用播放器播放 30 秒固定视频,采集 simpleperf。
记录:
top 10 函数
top 5 线程
libplayer_core.so 占比
mutex/memcpy/log 函数占比
做一次小优化,例如减少进度 JNI 回调频率,再采样对比。
工程风险与观测
性能优化也有风险。最常见的是“优化了平均值,恶化了尾部体验”。
所以每次性能改动都要记录:
优化前报告
优化后报告
平均帧耗时
P95 帧耗时
P99 帧耗时
掉帧数
崩溃率
如果一个改动让平均值下降,但 P99 上升,就要谨慎。播放器里偶发大卡顿比平均慢一点更难接受。
性能改动也要可回滚。建议每个优化点都能独立开关。
enable_batched_jni_events
enable_neon_yuv_path
enable_zero_copy_path
灰度期发现某设备异常,就能按能力降级,而不是整包回滚。
本章结论
simpleperf 的核心价值不是给你一个漂亮报告,而是建立“证据驱动优化”的习惯。先采样,再假设,再改动,再复测。native 性能优化只有闭环,才不会变成玄学。