标签:内核 序列 强制 end 检查 优化 使用情况 possible 效率
基础知识CPU 性能
在开发过程中,我们可以通过下面的方法获得设备的 CPU 信息。
// 获取 CPU 核心数
cat /sys/devices/system/cpu/possible
// 获取某个 CPU 的频率
cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
如果 CPU 使用率长期大于 60% ,表示系统处于繁忙状态,就需要进一步分析用户时间和系统时间的比例。对于普通应用程序,系统时间不会长期高于 30%,如果超过这个值,我们就应该进一步检查是 I/O 过多,还是其他的系统调用问题。
Android 是站在 Linux 巨人的肩膀上,虽然做了不少修改也砍掉了一些工具,但还是保留了很多有用的工具可以协助我们更容易地排查问题,这里我给你介绍几个常用的命令。例如,top 命令可以帮助我们查看哪个进程是 CPU 的消耗大户;vmstat 命令可以实时动态监视操作系统的虚拟内存和 CPU 活动;strace 命令可以跟踪某个进程中所有的系统调用。
除了 CPU 的使用率,我们还需要查看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 等待的时间
此外也可以通过 uptime 命令可以检查 CPU 在 1 分钟、5 分钟和 15 分钟内的平均负载。比如一个 4 核的 CPU,如果当前平均负载是 8,这意味着每个 CPU 上有一个线程在运行,还有一个线程在等待。一般平均负载建议控制在“0.7 × 核数”以内。
00:02:39 up 7 days, 46 min, 0 users,
load average: 13.91, 14.70, 14.32
另外一个会影响 CPU 饱和度的是线程优先级,线程优先级会影响 Android 系统的调度策略,它主要由 nice 和 cgroup 类型共同决定。nice 值越低,抢占 CPU 时间片的能力越强。当 CPU 空闲时,线程的优先级对执行效率的影响并不会特别明显,但在 CPU 繁忙的时候,线程调度会对执行效率有非常大的影响。
关于线程优先级,你需要注意是否存在高优先级的线程空等低优先级线程,例如主线程等待某个后台线程的锁。从应用程序的角度来看,无论是用户时间、系统时间,还是等待 CPU 的调度,都是程序运行花费的时间。
Android 卡顿排查工具
Traceview 和 systrace 都是我们比较熟悉的排查卡顿的工具,从实现上这些工具分为两个流派。
第一个流派是 instrument。获取一段时间内所有函数的调用过程,可以通过分析这段时间内的函数调用流程,再进一步分析待优化的点。
第二个流派是 sample。有选择性或者采用抽样的方式观察某些函数调用过程,可以通过这些有限的信息推测出流程中的可疑点,然后再继续细化分析。
这两种流派有什么差异?我们在什么场景应该选择哪种合适的工具呢?还有没有其他有用的工具可以使用呢?下面我们一一来看。
无论是哪种的 Traceview 对 release 包支持的都不太好,例如无法反混淆。其实 trace 文件的格式十分简单,之前曾经写个一个小工具,支持通过 mapping 文件反混淆 trace。
目前两个工具都只支持 debugable 的应用程序,如果想测试 release 包,需要将测试机器 root。对于这个限制,我们在实践中会专门打出 debugable 的测试包,然后自己实现针对 mapping 的反混淆功能。
随着 Android 版本的演进,Google 不仅提供了更多的性能分析工具,而且也在慢慢优化现有工具的体验,使功能更强大、使用门槛更低。而 Android Studio 则肩负另外一个重任,那就是让开发者使用起来更加简单的,图形界面也更加直观。
在 Android Studio 3.2 的 Profiler 中直接集成了几种性能分析工具,其中:
? Sample Java Methods 的功能类似于 Traceview 的 sample 类型。
? Trace Java Methods 的功能类似于 Traceview 的 instrument 类型。
? Trace System Calls 的功能类似于 systrace。
? SampleNative (API Level 26+) 的功能类似于 Simpleperf。
坦白来说,Profiler 界面在某些方面不如这些工具自带的界面,支持配置的参数也不如命令行,不过 Profiler 的确大大降低了开发者的使用门槛。
另外一个比较大的变化是分析结果的展示方式,这些分析工具都支持了 Call Chart 和 Flame Chart 两种展示方式。下面我来讲讲这两种展示方式适合的场景。
Call Chart 就像给应用程序做一个心电图,我们可以看到在这一段时间内,各个线程的具体工作,比如是否存在线程间的锁、主线程是否存在长时间的 I/O 操作、是否存在空闲等。
当我们不想知道应用程序的整个调用流程,只想直观看出哪些代码路径花费的 CPU 时间较多时,火焰图就是一个非常好的选择。例如,之前我的一个反序列化实现非常耗时,通过火焰图发现耗时最多的是大量 Java 字符串的创建和拷贝,通过将核心实现转为 Native,最终使性能提升了很多倍。
火焰图还可以使用在各种各样的维度,例如内存、I/O 的分析。有些内存可能非常缓慢地泄漏,通过一个内存的火焰图,我们就知道哪些路径申请的内存最多,有了火焰图我们根本不需要分析源代码,也不需要分析整个流程。
最后我想说,每个工具都可以生成不同的展示方式,我们需要根据不同的使用场景选择合适的方式。
标签:内核 序列 强制 end 检查 优化 使用情况 possible 效率
原文地址:https://blog.51cto.com/14311301/2386782