安卓性能測試的重要方面是對各項性能指標的采集和分析,如常見性能指標內存、cpu、電量、流量等,本文整理了cpu占有率統(tǒng)計方法和基本原理。
安卓性能指標cpu主要關注兩點:
(1)cpu總體使用率(2)應用程序cpu占用率。
cpu指標的查看方式有多種,最直接的就是android自帶的DDMS可視化工具,也可以在IDE(Android Studio)的Monitor中實時查看。另外就是通過linux系統(tǒng)/proc/stat和/proc/<pid>/stat文件進行占用率的計算,也可以利用top命令或者dumpsys cupinfo等命令實時查看當前cpu情況。我們接下來詳細看下每一種方法是如何查看和獲得cpu占用率數(shù)據(jù)。
一、DDMS等可視化工具
我們可以通過安卓SDK自帶的DDMS工具查看當前安卓應用程序的CPU和內存使用情況:


另外也可以通過Android Studio自帶的cpu Monitor來實時查看CPU使用情況:

二、/proc/stat及/proc/<pid>/stat文件計算
/proc文件系統(tǒng)是一個偽文件系統(tǒng),它只存在內存當中,而不占用外存空間。它以文件系統(tǒng)的方式為內核與進程提供通信的接口。用戶和應用程序可以通過/proc得到系統(tǒng)的信息,并可以改變內核的某些參數(shù)。由于系統(tǒng)的信息,如進程,是動態(tài)改變的,所以用戶或應用程序讀取/proc目錄中的文件時,/proc文件系統(tǒng)是動態(tài)從系統(tǒng)內核讀出所需信息并提交的。
我們關注的安卓性能指標cpu關注的cpu總體使用率和應用程序cpu占用率主要與兩個proc文件相關,分別是/proc/stat和/proc/<pid>/stat文件(pid是該應用程序的進程號)。
1./proc/stat與cpu總體使用率的計算
/proc/stat文件包含了所有CPU活動的信息,該文件中的所有值都是從系統(tǒng)啟動開始累計到當前時刻的統(tǒng)計值。不同內核版本中該文件的格式可能不大一致,以下通過實例來說明數(shù)據(jù)該文件中各字段的含義。
不管是mac還是windows,我們是無法直接看到/proc/stat文件的,我們可以使用adb shell命令進入Android shell命令模式(可以理解成進入當前設備的Linux Shell系統(tǒng))進行查看,這時候模擬器或者手機要啟用并且正確接通。

進入目標設備的Linux Shell環(huán)境, 在該環(huán)境中可以執(zhí)行一些Linux命令。如在Linux Shell環(huán)境中執(zhí)行ps可以查看android設備中運行的所有進程。在Linux Shell環(huán)境中執(zhí)行exit可以退出Linux Shell環(huán)境。
1.1 /proc/stat文件分析
我們在Linux Shell環(huán)境中使用cat命令查看/proc/stat文件。

該文件顯示的第一行就是cpu總體的使用情況,每列數(shù)值是后面幾行邏輯處理器每列數(shù)據(jù)的相加值,可見該cpu共有4個邏輯處理器(Processor),我們也可以通過/proc/cpuinfo文件去查看該Linux內核的cpu具體情況。我們依據(jù)Linux用戶手冊詳細看下第一行cpu數(shù)據(jù)的每列數(shù)據(jù)代表的含義。
1)這些數(shù)值的單位都是 jiffies,jiffies 是內核中的一個全局變量,用來記錄系統(tǒng)啟動以來產(chǎn)生的節(jié)拍數(shù),在 Linux 中,一個節(jié)拍大致可以理解為操作系統(tǒng)進程調度的最小時間片,不同的 Linux 系統(tǒng)內核這個值可能不同,通常在 1ms 到 10ms 之間。
2)cpu 552 56 1496 266573 652 0 224 0 0 0
**user(552) ** Time spent in user mode.
從系統(tǒng)啟動開始累積到當前時刻,處于用戶態(tài)的運行時間,不包含 nice 值為負的進程。
nice(56) Time spent in user mode with low priority(nice).
系統(tǒng)啟動開始累積到當前時刻,nice 值為負的進程所占用的 CPU 時間。Nice值是類UNIX操作系統(tǒng)中表示靜態(tài)優(yōu)先級的數(shù)值。 每個進程都有自己的靜態(tài)優(yōu)先級,優(yōu)先級高的進程得以優(yōu)先運行。
system(1496) Time spent in system mode.
從系統(tǒng)啟動開始累積到當前時刻,處于核心態(tài)的運行時間。
idle(266573) Time spent in the idle task.
從系統(tǒng)啟動開始累積到當前時刻,除 IO 等待時間以外的其他等待時間。
**iowait(652) ** Time waiting for I/O to complete.
從系統(tǒng)啟動開始累積到當前時刻,IO 等待時間。(since 2.5.41,內核版本,下同)
**irq(0) ** Time servicing interrupts.
從系統(tǒng)啟動開始累積到當前時刻,服務中斷時間。(since 2.6.0-test4)
softirq(224) Time servicing softirqs.
從系統(tǒng)啟動開始累積到當前時刻,軟中斷時間。(since 2.6.0-test4)
**stealstolen(0) ** Stolen time, which is the time spent in other operating systems when running in a virtualized environment.
從系統(tǒng)啟動開始累積到當前時刻,在虛擬環(huán)境運行時花費在其他操作系統(tǒng)的時間。(since 2.6.11)
**guest(0) ** Which is the time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
從系統(tǒng)啟動開始累積到當前時刻,在Linux內核控制下的操作系統(tǒng)虛擬cpu花費的時間。(since 2.6.24)
guest_nice Time spent running a niced guest (virtual CPU for guest operating systems under the control of the Linux kernel).
從系統(tǒng)啟動開始累積到當前時刻,在Linux內核控制下的操作系統(tǒng)虛擬cpu花費在nice進程上的時間。(since Linux 2.6.33)
PS:這里涉及到Linux內核的版本情況,我們可以通過uname -a和/proc/version來查看自己Android設備的Linux內核情況。下圖為我的Android設備(genymotion模擬器)的Linux內核情況,可見我的內核版本是3.10版本,所以我查看到的/proc/stat文件里首行會顯示所有的列數(shù)據(jù)。

其他行數(shù)據(jù)可以通過繼續(xù)Linux用戶手冊來查看。
1.2 cpu總體使用率的計算
cpu總的使用時間計算如下:
totalCPUTime = user + nice + system + idle + iowait + irq + softirq + stealstolen + guest + guest_nice
也就是/proc/stat的首行所有列數(shù)據(jù)的加和值。我們的目標是計算總體cpu使用率。
通常我們都會選擇較短的時間進行取樣兩次cpu數(shù)據(jù)1和2。
常用cpu占用率方法可描述為:
totalCPUrate = (非空閑cpu時間2-非空閑cpu時間1)/(cpu總時間2-cpu總時間1)x100%
換成變量描述為:
totalCPUrate =( (totalCPUTime2-idle2)-(totalCPUTime1-idle1))/(totalCPUTime2-totalCPUTime1)x100%
那么根據(jù)/proc/stat文件可以得到cpu總體使用率的計算方法:
- 采樣兩個足夠短的時間間隔的cpu數(shù)據(jù),分別記作t1、t2,其中t1、t2的結構均為:
(user、nice、system、idle、iowait、irq、softirq、stealstolen、guest、guest_nice)的10元組;(當然這里依據(jù)Linux內核的不同有些數(shù)據(jù)可能沒有,就不必計入) - 計算t1、t2總的cpu時間片totalCPUTime
a) 把第一次的所有cpu10元組數(shù)據(jù)求和,得到totalCPUTime1;
b) 把第二次的所有cpu10元組數(shù)據(jù)求和,得到totalCPUTime2; - 計算空閑時間idle
cpu空閑時間對應第四列的數(shù)據(jù)
a)獲得第一次的idle數(shù)據(jù),記為idle1
b)獲得第二次的idle數(shù)據(jù),記為idle2 - 計算cpu使用率
totalCPUrate = ((totalCPUTime2-idle2)-(totalCPUTime1-idle1))/(totalCPUTime2-totalCPUTime1)x100%
了解了整體的計算方法后,我們可以使用java代碼進行簡要實現(xiàn)。
1.3 cpu總體使用率計算的java實現(xiàn)
核心的計算方法抽取出來如下:(參考了騰訊GT的CPU數(shù)據(jù)實現(xiàn)方法)
public String getCpuUsage() {
double usage = 0.0;
boolean initCpu = true;
if (initCpu) {
initCpu = false;
RandomAccessFile reader = null;
reader = new RandomAccessFile("/proc/stat","r");
String load = reader.readLine();
String[] toks = load.split(" ");
o_idle = Double.parseDouble(toks[5]);
o_cpu = Double.parseDouble(toks[2])
+ Double.parseDouble(toks[3])
+ Double.parseDouble(toks[4])
+ Double.parseDouble(toks[6])
+ Double.parseDouble(toks[7])
+ Double.parseDouble(toks[8])
+ Double.parseDouble(toks[9]);
FileUtil.closeRandomAccessFile(reader);
} else {
RandomAccessFile reader = null;
reader = new RandomAccessFile("/proc/stat", "r");
String load;
load = reader.readLine();
String[] toks = load.split(" ");
double c_idle = Double.parseDouble(toks[5]);
double c_cpu = Double.parseDouble(toks[2])
+ Double.parseDouble(toks[3])
+ Double.parseDouble(toks[4])
+ Double.parseDouble(toks[6])
+ Double.parseDouble(toks[7])
+ Double.parseDouble(toks[8]);
+ Double.parseDouble(toks[9]);
if (0 != ((c_cpu + c_idle) - (o_cpu + o_idle))) {
usage = DoubleUtils.div((100.00 * ((c_cpu - o_cpu))),
((c_cpu + c_idle) - (o_cpu + o_idle)), 2);
}
o_cpu = c_cpu;
o_idle = c_idle;
FileUtil.closeRandomAccessFile(reader);
}
return String.valueOf(usage) + "%";
}```
需要注意的是,上述代碼只是一個核心算法的示例,真正實現(xiàn)的話還需要考慮Linux內核的不同導致取值范圍的不同,另外計算出usage值為負或者為大于100的特殊情況處理。
#### 2./proc/<pid>/stat與應用程序cpu占有率的計算
/proc/<pid>/stat文件包含了某一進程所有的活動的信息,該文件中的所有值都是從系統(tǒng)啟動開始累計到當前時刻的統(tǒng)計值。以下通過實例數(shù)據(jù)來說明該文件中各字段的含義。
##### 2.1 /proc/<pid>/stat文件分析
我們在Linux Shell環(huán)境中使用cat命令查看/proc/<pid>/stat文件。我們可以先通過top命令獲得目標應用的進程id。

這里我們的目標進程id是1378。這時候我們去內存中查看/proc/1378/stat文件:

根據(jù)[Linux用戶手冊](http://man7.org/linux/man-pages/man5/proc.5.html)我們可以看到/proc/1387/stat文件中各個字段代表的含義,其中與我們計算應用程序cpu占用率相關的數(shù)據(jù)主要有如下幾個:
>pid=1378 進程號
utime=1286 該任務在用戶態(tài)運行的時間,單位為jiffies
stime=1854 該任務在核心態(tài)運行的時間,單位為jiffies
cutime=6 所有已死線程在用戶態(tài)運行的時間,單位為jiffies
cstime=2 所有已死在核心態(tài)運行的時間,單位為jiffies
##### 2.2 cpu總體使用率的計算
應用程序cpu的使用時間計算如下:
>processCPUTime = utime + stime,該值包括其所有線程的cpu時間。
通常我們都會選擇較短的時間進行取樣兩次總體cpu數(shù)據(jù)和應用程序cpu數(shù)據(jù),分別記為1和2。常用應用程序cpu占用率方法可描述為:
>processCPUrate = ( processCPUTime2 – processCPUTime1) / (totalCPUTime2 – totalCPUTime1) x100%
那么根據(jù)/proc/stat文件和/proc/<pid>/stat文件可以得到某應用程序cpu占用率的計算方法:
1. **采樣兩個足夠短的時間間隔的cpu數(shù)據(jù)和進程cpu數(shù)據(jù),分別記作t1、t2,其中t1、t2的cpu數(shù)據(jù)結構均為:
(user、nice、system、idle、iowait、irq、softirq、stealstolen、guest、guest_nice)的10元組;t1、t2的進程cpu數(shù)據(jù)結構為(utime、stime)的2元組。**
2. **計算t1、t2總的cpu時間totalCPUTime和進程cpu時間processCPUTime
a) 把第一次的所有cpu10元組數(shù)據(jù)求和,得到totalCPUTime1;
b) 把第二次的所有cpu10元組數(shù)據(jù)求和,得到totalCPUTime2;
c) 把第一次的所有進程cpu4元組數(shù)據(jù)求和,得到processCPUTime1;
d) 把第二次的所有進程cpu4元組數(shù)據(jù)求和,得到processCPUTime2**
3. **計算應用程序cpu使用率
processCPUrate = ( processCPUTime2 – processCPUTime1) / (totalCPUTime2 – totalCPUTime1) x100%**
了解了整體的計算方法后,我們可以使用java代碼進行簡要實現(xiàn)。
##### 2.3 cpu總體使用率計算的java實現(xiàn)
核心的計算方法抽取出來如下:(參考了騰訊GT的CPU數(shù)據(jù)實現(xiàn)方法)
public String getProcessCpuUsage(int pid) {
String result = "";
String[] result1 = null ;
String[] result2 = null;
if (pid >= 0) {
result1 = getProcessCpuAction(pid);
if (null != result1) {
pCpu = Double.parseDouble(result1[1])
+ Double.parseDouble(result1[2]);
}
result2 = getCpuAction();
if (null != result2) {
aCpu = 0.0;
for (int i = 2; i < result2.length; i++) {
aCpu += Double.parseDouble(result2[i]);
}
}
double usage = 0.0;
if ((aCpu - o_aCpu) != 0) {
usage = DoubleUtils.div(((pCpu - o_pCpu) * 100.00),
(aCpu - o_aCpu), 2);
}
o_pCpu = pCpu;
o_aCpu = aCpu;
result = String.valueOf(usage) + "%";
}
p_jif = pCpu;
return result;
}```
這里getProcessCpuAction(pid)和getCpuAction()分別處理/proc/<pid>/stat文件和/proc/stat文件,將應用程序的cpu和總體cpu數(shù)據(jù)分別保存在result1和result2變量中,然后進行計算。由于篇幅有限,處理文件的方法具體實現(xiàn)就不展開了。
三、基本命令獲取cpu使用情況
除了可以從/proc文件系統(tǒng)中計算出cpu使用率外,我們也可以采用直觀簡單的命令來拿到cpu使用情況。當然了,不管是Linux還是Android系統(tǒng)提供的命令行查看,都是基于proc文件系統(tǒng)計算得到的,不同命令方法獲得的數(shù)據(jù)可能會有差異,甚至與自己計算的也有不同,這是因為采用不同的計算方法和計算精度得到的,誤差可接受。
1.top命令
top命令是Linux下常用的性能分析工具,能夠實時顯示系統(tǒng)中各個進程的資源占用狀況,類似于Windows的任務管理器。top是一個動態(tài)顯示過程,即可以通過用戶按鍵來不斷刷新當前狀態(tài)。如果在前臺執(zhí)行該命令,它將獨占前臺,直到用戶終止該程序為止。top命令提供了實時的對系統(tǒng)處理器的狀態(tài)監(jiān)視。它將顯示系統(tǒng)中CPU最“敏感”的任務列表。該命令可以按CPU使用、內存使用和執(zhí)行時間對任務進行排序。

使用top命令,可以讓我們一眼直觀地看到應用程序cpu占用率的情況。
PID是進程的ID,是唯一的
CPU不用說了,就是CPU占用比率
VSS(Virtual Set Size)虛擬耗用內存(包含共享庫占用的內存)
PSS(Proportional Set Size)實際使用的物理內存(比例分配共享庫占用的內存)
RSS(Resident Set Size)實際耗用的物理內存(包含共享庫占用的內存)
USS(Unique Set Size)進程獨自占用的物理內存(不包含共享庫占用的內存)
使用此方法的優(yōu)點就是獲取信息簡單容易。使用次方法的缺點也一目了然,就是精確度不是很高。另外一些較為常用的top命令:
可查看占用cpu最高的前10個程序(-t 顯示進程名稱,-s 按指定行排序,-n 在退出前刷新幾次,-d 刷新間隔,-m 顯示最大數(shù)量):
top -m 10 -s cpu
如果你想篩選出你自己的應用的話可以用下面這一命令:
adb shell top -n 1| grep PackageName
2.dumpsys cpuinfo命令
Android提供的dumpsys工具可以用于查看感興趣的系統(tǒng)服務信息與狀態(tài),dumpsys cpuinfo可以用來查看安卓系統(tǒng)當前的cpu使用情況。

這個能夠統(tǒng)計到所有應用的CPU占用信息,要比top -n 1獲得的信息更加詳細一些。
缺點就是這種方法會有權限問題,ROOT了之后才能使用。
以上便是安卓端統(tǒng)計cpu占用率的主要方法,拿到數(shù)據(jù)比較簡單,難的是如何分析拿到的cpu數(shù)據(jù),找到性能瓶頸點和優(yōu)化點,這也是做安卓性能測試的重要方向。
參考文章:
http://www.blogjava.net/fjzag/articles/317773.html
http://liandongyang.coding.me/post/%E5%85%B3%E4%BA%8EAndroid%E7%9A%84CPU%E5%86%85%E5%AD%98%E7%9B%91%E6%8E%A7/http://blog.csdn.net/q838197181/article/details/50622498