App性能测试——CPU使用率

CPU使用率是性能测试是一项重要指标,CPU占用过高会使得设备运行程序出现卡顿与发热,甚至出现应用程序Crash,影响用户体验。在排除硬件环境的限制下,应用程序应该尽可能少的占用CPU

Android CPU占用

CPU使用率原理

Android系统内核是基于Liunx,在Linux系统下CPU利用率分为用户态、系统态、空闲态,分别表示CPU处于用户态执行的时间,系统内核执行的时间,和空闲系统进程执行的时间。

平时所说的CPU利用率是指:CPU执行非系统空闲进程的时间 / CPU总的执行时间。那么这里所说的时间含义是什么呢?

  • HZLinux 核心每隔固定周期会发出timer interrupt (时钟中断),HZ是用来定义每一秒有几次时钟中断。例如HZ1000,就代表每秒有1000次时钟中断。

  • Jiffies:在Linux的内核中,有一个全局变量:JiffiesJiffies代表时间。它的单位随硬件平台的不同而不同。jiffies的单位就是 1/HZ

  • Intel平台jiffies的单位是1/100秒,这就是系统所能分辨的最小时间间隔了。每个CPU时间片,Jiffies都要加1

  • 那么CPU利用率计算公式如下:

1
CPU使用率=(用户态Jiffies+系统态Jiffies)/总Jiffies

CPU测试方法

adb 命令

由于Android是基于Linux内核改造而成的操作系统,自然而然也能使用Linux的一些常用命令。比如我们可以使用top命令查看哪些进程是 CPU 的主要消耗者。

Top命令使用方法如下:

1
2
3
4
5
6
7
8
>adb shell top -h
Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [-t ] [ -h ]
-m num Maximum number of processes to display. 最多显示多少个进程
-n num Updates to show before exiting. 刷新次数
-d num Seconds to wait between updates. 刷新间隔时间(默认5秒)
-s col Column to sort by (cpu,vss,rss,thr). 按哪列排序
-t Show threads instead of processes. 显示线程信息而不是进程
-h Display this help screen. 显示帮助文档

注意:由于Android 8.0以后Google的权限限制,再也拿不到进程CPU的实时占用率,只能拿到自己本身进程的Jiffies,而由于拿不到系统整体Jiffies的情况下,就没办法衡量CPU当前的消耗状况了,也没办法根据当前CPU状态实时做一些策略调整。

输入adb shell top命令可以看到如下所示数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
User 22%, System 20%, IOW 0%, IRQ 7%
User 247 + Nice 0 + Sys 226 + Idle 562 + IOW 0 + IRQ 0 + SIRQ 81 = 1116

PID USER PR NI CPU% S #THR VSS RSS PCY Name
299 mediacod 20 0 21% S 9 136660K 40632K fg media.codec
3691 u0_a64 1 -19 17% R 265 2680980K 677280K fg com.youku.phone
280 root 20 0 5% S 34 216848K 21756K fg /system/bin/local_opengl
305 system 12 -8 2% S 13 73764K 6616K fg /system/bin/surfaceflinger
293 audioser 20 0 0% S 9 38288K 9656K fg /system/bin/audioserver
554 system 20 0 0% S 88 1290680K 177748K fg system_server
1284 u0_a65 16 -4 0% S 131 1692856K 228292K bg com.hunantv.imgo.activity
20 root 20 0 0% S 1 0K 0K fg ksoftirqd/3
7 root 20 0 0% S 1 0K 0K fg rcu_preempt
23799 root 20 0 0% R 1 6272K 2232K fg top
2028 u0_a69 20 0 0% S 69 1653892K 254308K bg com.tal.kaoyan
683 media_rw 20 0 0% S 4 9628K 3484K fg /system/bin/sdcard
138 root 20 0 0% S 7 16540K 1272K fg /sbin/adbd
327 root 20 0 0% S 1 3404K 852K fg /sbin/healthd
22 root 0 -20 0% S 1 0K 0K fg kworker/3:0H
23 root 0 -20 0% S 1 0K 0K fg perf
24 root 0 -20 0% S 1 0K 0K fg writeback
25 root 25 5 0% S 1 0K 0K fg ksmd
27 root 0 -20 0% S 1 0K 0K fg crypto
28 root 0 -20 0% S 1 0K 0K fg kintegrityd
29 root 0 -20 0% S 1 0K 0K fg bioset
30 root 0 -20 0% S 1 0K 0K fg kblockd
  1. 第一组数据的含义
  • User: 处于用户态的运行时间,不包含优先值为负进程
  • Nice: 优先值为负的进程所占用的CPU时间
  • Sys: 处于核心态的运行时间
  • Idle: 除IO等待时间以外的其它等待时间
  • IOW: IO等待时间
  • IRQ: 硬中断时间
  • SIRQ: 软中断时间
  1. 第二组数据含义
  • PID: 进程id
  • USER: 进程所有者
  • NInice值。负值表示高优先级,正值表示低优先级
  • PR: 优先级
  • CPU%: 当前瞬时CPU占用率
  • S : 进程状态:D=不可中断的睡眠状态, R=运行, S=睡眠, T=跟踪/停止, Z=僵尸进程
  • #THR: 程序当前所用的线程数
  • VSS : Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
  • RSS: Resident Set Size 实际使用物理内存(包含共享库占用的内存)
  • PCY: 调度策略优先级。
  • UID: 进程所有者的用户id
  • Name: 进程的名称
  1. 我们可以还使用findstr命令获取某个应用的CPU占用率。
1
2
λ adb shell top -m 100 -n 1 -d 1 -s cpu | findstr com.youku.phone
3691 u0_a64 1 -19 40% S 331 2812328K 660792K fg com.youku.phone

iOS CPU使用率

iOS系统架构

iOS系统架构主要由以下四层组成:

ios-frame

  • UI层: 主要有SpringBoard、Spotlight等UI交互界面
  • 应用框架层:主要有 Cocoa Touch
  • 核心框架层:主要有 OpenGL等图形、多媒体组件
  • Darwin:操作系统核心。

Darwin

iOS 是基于 Apple Darwin 内核,Darwin的内核是XNU(类Unix)。XNU是两种技术的混合体:MachBSDBSD层确保了DarwinUNIX特性,真正的内核是Mach,但是对外部隐藏。

BSD以上属于用户态,所有的内容都可以被应用程序访问,而应用程序不能访问内核态。当需要从用户态切换到内核态的时候,需要通过mach trap实现切换。

Apple Darwin

Mach 内核是轻量级的平台,只能完成操作系统最基本的职责,如:进程和线程、虚拟内存管理、任务调度、进程通信和消息传递机制。其他的工作,如文件操作和设备访问,都是由 BSD 层实现。

事实上,Mach 并不能识别 UNIX 中的所有进程,而是采用一种稍微不同的方式,使用了比进程更轻量级的概念:任务(Task)。

iOS App线程

经典的 UNIX 采用了自上而下的方式:最基本的对象是进程,然后进一步划分为一个或多个线程;Mach 则采用了自底向上的方式:最基本的单元是线程,一个或多个线程包含在一个任务中。

因此iOS App 作为进程运行时会有多个线程,每个线程对 CPU 的使用率不同。各个线程对 CPU 使用率的总和,就是当前 AppCPU 的占用率

iOS测试利器-Instruments

Instruments简介

Instruments 是 Xcode 的一个工具集,为我们提供了强大的程序性能分析及测试能力。
使用 Instruments 你可以做下面这些事:

  • 检查一个或多个应用或进程的行为。
  • 检查设备相关的功能,比如:Wi-Fi、蓝牙等。
  • 在真机或模拟器上进行性能测试。
  • 创建自定义的 DTrace 来分析系统以及应用的各种行为。
  • 跟踪源码中的问题。
  • 对 App 进行性能分析
  • 查找 App 中的内存问题,比如:内存泄露(Leaked memory)、废弃内存(Abandoned memory)、僵尸(zombies)等。
  • 进行系统级别的问题定位。
  • 通过脚本记录一个用户行为序列,从而可以通过运行脚本对你的 iOS 应用进行自动化测试。
  • 保存测试配置模板以供复用。

Instruments工具集

在Xcode中点击菜单Open Developer tool-> Instruments即可打开,如下图所示:

Instruments

  1. 从上图中我们可以看到Instruments有许多菜单,不同菜单支持的功能如下所示:

xcode-instruments-tools

CPU占用率测试

  1. 使用Time Profiler可以来监测CPU使用统计信息,如下图所示选择选择点击红色按钮运行后,就能得到 CPU 性能的结果了。

instruments_trace_document_toolbar_target_menus

  1. 注意:测试的app 需要使用debug包否则会出现如下报错。

ios-debug

time-profiler-error

  1. 如下图所示:可以查看运行过程中CPU使用率情况,底部可以详细看到应用每个线程占用的CPU。

Time Profiler cpu

  1. 我们还能在时间轴面板里面去选择一段时间来查看该时间段里更为细节的 CPU 性能:

cpu -sepecial

  1. 数据导航栏菜单含义如下:
  • Weight :调用它自身和它的子类花费的时间以及占总时间的百分比。
  • Self-Weight :自身花费的时间。
  • Symbol Name : 被调用的名字。
  1. 关于Call Tree有如下选项:
  • Separate by State(不建议选择):通过状态分类查看CPU占用。
  • Separate by Thread(默认选择):通过线程分类来查看那些线程占用CPU最多。
  • Invert Call Tree(不建议选择):调用树倒返过来,如选上就会返过来从最底层调用向上一级一级的显示。如果想要查看那个方法调用为最深时使用会更方便些。
  • Hide System Libraries(建议选择):选上它只会展示与应用有关的信息,一般情况下我们只关心自己写的代码所需的耗时,而不关心系统库的CPU耗时。
  • Flatten Recursion(一般不选):选上它会将调用栈里递归函数作为一个入口。
  • Top Functions(一般不选):选上它会将最耗时的函数降序排列,而这种耗时是累加的,比如A调用了B,那么A的耗时数是会包含B的耗时数。

参考资料