影响范围,现象
“我的后羿怎么动不了!”
“”应用启动怎么那么慢?”
“滑动的时候怎么那么卡?”
影响用户体验
基础知识
1. CPU 性能
// 获取 CPU 核心数
cat /sys/devices/system/cpu/possible
// 获取某个 CPU 的频率
cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
现代芯片不仅带有强大的 GPU,还配备了专门为神经网络计算打造的 NPU(Neural network Processing Unit)。
小目标:边缘计算
利用移动端的计算能力来降低高昂的服务器成本。
开发中,看 CPU 性能 下饭。
eg : 线程池使用线程数根据 CPU 的核心数,一些高级的AI 功能只在主频比较高或者带有 NPU 的设备开启。
2. 卡顿问题分析指标
CPU 的使用率
整个系统的 CPU 使用情况:cat /proc/stat
cat proc/self/stat:
utime: 用户时间,反应用户代码执行的耗时
stime: 系统时间,反应系统调用执行的耗时
majorFaults:需要硬盘拷贝的缺页次数
minorFaults:无需硬盘拷贝的缺页次数
某个进程的 CPU 使用情况:
cat /proc/[pid]/stat
**问题:**不同内核版本 /proc/stat 文件格式不一致。/proc/stat 文件中第一行是总的 CPU 使用情况。
Linux CPU 占用率
Linux 文档
分析卡顿原因
跟 CPU、内存、磁盘 I/O 都可能有关,跟用户当时的系统环境也有很大关系。
造成卡顿的原因可能有千百种,不过最终都会反映到 CPU 时间上。
可以把 CPU 时间分为两种:用户时间和系统时间。
CPU 占用率分析
如果 CPU 使用率长期大于 60% ,表示系统处于繁忙状态,就需要进一步分析用户时间和系统时间的比例。
对于普通应用程序,系统时间不会长期高于 30%,如果超过这个值,我们就应该进一步检查是 I/O 过多,还是其他的系统调用问题。
top 命令:eg top -m 10
vmstat 命令: 可以实时动态监视操作系统的虚拟内存和 CPU 活动。
strace 命令: 可以跟踪某个进程中所有的系统调用。
CPU 饱和度 :
CPU 饱和度反映的是线程排队等待 CPU 的情况,也就是 CPU 的负载情况。
CPU 饱和度首先会跟应用的线程数有关,如果启动的线程过多,,容易导致系统不断地切换执行的线程,把大量的时间浪费在上下文切换,我们知道每一次 CPU 上下文切换都需要刷新寄存器和计数器,至少需要几十纳秒的时间。
可以通过使用vmstat命令或者/proc/[pid]/schedstat文件来查看 CPU 上下文切换次数,这里特别需要注意nr_involuntary_switches被动切换的次数。
/proc/self/sched:
nr_voluntary_switches:
主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是 IO。
nr_involuntary_switches:
被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占 CPU。
se.statistics.iowait_count:IO 等待的次数
se.statistics.iowait_sum: IO 等待的时间
/proc/[pid]/stat // 进程 CPU 使用情况
/proc/[pid]/task/[tid]/stat // 进程下面各个线程的 CPU 使用情况
/proc/[pid]/sched // 进程 CPU 调度相关
/proc/loadavg // 系统平均负载,uptime 命令对应文件
线程优先级
需要注意**是否存在高优先级的线程空等低优先级线程,例如主线程等待某个后台线程的锁。**从应用程序的角度来看,无论是用户时间、系统时间,还是等待 CPU 的调度,都是程序运行花费的时间。
Android 卡顿排查工具
Traceview 和 systrace 等,从实现上分2个流派。
第一个流派是 instrument。获取一段时间内所有函数的调用过程,可以通过分析这段时间内的函数调用流程,再进一步分析待优化的点。
第二个流派是 sample。有选择性或者采用抽样的方式观察某些函数调用过程,可以通过这些有限的信息推测出流程中的可疑点,然后再继续细化分析。
1、Traceview
Traceview 属于 instrument 类型,它可以用来查看整个过程有哪些函数调用,但是工具本身带来的性能开销过大,有时无法反映真实的情况。
2、Nanoscope
instrument 类型
性能损耗比较小。
它的实现原理是直接修改 Android 虚拟机源码,在ArtMethod执行入口和执行结束位置增加埋点代码,将所有的信息先写到内存,等到 trace 结束后才统一生成结果文件。
但是 trace 结束生成结果文件这一步需要的时间比较长。
另一方面它可以支持分析任意一个应用,可用于做竞品分析。
限制:
- 需要自己刷 ROM,并且当前只支持 Nexus 6P,或者采用其提供的 x86 架构的模拟器。
- 默认只支持主线程采集,其他线程需要代码手动设置。考虑到内存大小的限制,每个线程的内存数组只能支持大约 20 s 左右的时间段。
Uber 写了一系列自动化脚本协助整个流程,使用起来还算简单。
可以 每天定期自动跑,启动测试。
3、systrace
sample 类型
4、Simpleperf
Android 5.0 新增 性能分析工具。
可以看到所有 Native 代码耗时。
封装了 systrace 的监控功能。
目前除了 Nanoscope 之外的三个工具都只支持debugable 的应用程序,如果想测试 release 包,需要将测试机器 root。
5、Profile
Facebook 开源了一个叫Profilo的库,它收集了各大方案的优点.
不用插桩、性能基本没有影响、捕捉信息还全,那 Profilo 不就是完美的化身吗?当然由于它利用了大量的黑科技,兼容性是需要注意的问题。它内部实现有大量函数的 Hook,unwind 也需要强依赖 Android Runtime 实现。Facebook 已经将 Profilo 投入到线上使用,但由于目前 Profilo 快速获取堆栈功能依然不支持 Android 8.0 和 Android 9.0,鉴于稳定性问题,建议采取抽样部分用户的方式来开启该功能。
选择哪种工具:
需要看具体的场景。汇总一下,如果需要分析 Native 代码的耗时,可以选择 Simpleperf;如果想分析系统调用,可以选择 systrace;如果想分析整个程序执行流程的耗时,可以选择 Traceview 或者插桩版本的 systrace。
Android Studio 3.2 的 Profiler中直接集成了几种性能分析工具,其中:
- Sample Java Methods 的功能类似于 Traceview 的 sample 类型。
- Trace Java Methods 的功能类似于 Traceview 的 instrument 类型。
- Trace System Calls 的功能类似于 systrace。
- SampleNative (API Level 26+) 的功能类似于 Simpleperf。
- Profilo
实现了快速获取 Java 堆栈,其实它参考的是 JVM 的 AsyncGetCallTrace 思路,然后适配 Android Runtime 的实现。 - systrace
使用的是 Linux 的 ftrace 。 - Simpleperf
参考了 Linux 的 perf 工具 。
我们很多创新性的东西,其实还是基于 Java 和 Linux 十年前的产物。
解决方案
先试试
https://github.com/AndroidAdvanceWithGeektime/Chapter06
心得:把握好 哪些需要 掌握,哪些需要 知道。