
由于Android 8.0以后Google的权限限制,SDK再也拿不到进程CPU的实时占用率,只能拿到自己本身进程的Jiffies,而由于拿不到系统整体Jiffies的情况下,就没办法衡量CPU当前的消耗状况了,也没办法根据当前CPU状态实时做一些策略调整。因此进行深入研究以后,给出Android 8.0以后判断CPU状态的几个参考方案(非标准答案)。
1)Jiffies 全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。
2)Tick HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒。
由于我们可以控制一条汇编指令,在任意型号CPU上是都是在一个时钟周期内完成的(原子化操作),比如:

因此,在这里每一次CPU数据采样都是传循环参数10000次,也就是执行add指令130*10000次,然后我们循环执行20轮,计算出执行130*10000指令花费的最小一轮(130*10000条指令)时间min_time_s

每一条指令消耗的时间为:

而cpu频率则是cost的倒数,也就是单位时间(s)内执行cycles的数量

执行130*10000次




我们可以看看top命令的源码


可以看到TOP本身也是拿到/proc/stat后统计的。在这里我有一个问题也没弄明白,就是在代码内部我有权限执行top命令,此时top命令是当前进程fork出来的,应该理论上是继承了父进程的权限才对,但实际上父进程没有权限访问/proc/stat。所以本想把top的具体实现挪出来,目前只能直接驱动top命令。因此这种方案应用于实时性以及性能开销要求不高的监控程序。

这种方案使用场景比较受限,用来判断当前进程的性能消耗比较准确。但是如果该设备别的应用程序导致CPU占比很高,但是自己程序的Jiffies值很小,就误以为设备不繁忙然后开了一堆线程过去,那可能设备就会挂掉。具体可以看后续的实验,在这里也给出一段参考代码:

由于CPU的频率档位是离散的,因此各个核的频率变化不是连续的,而是一个离散的值,而且由于Android为了Linux系统稳定运行,会对几个核锁频。那怎么计算CPU的频率呢? 有下面几个步骤:
1)拿到各个核心的当前频率
2)拿到CPU的频率档位
3)解析当前进程/线程被系统分派到哪几个核上

如果想看线程纬度的,可以执行cat /proc/$pid/task/$tid/status。
在这里需要了解一个概念叫CPU亲和性,CPU 亲和性(affinity) 就是进程要在某个给定的CPU上尽量长时间地运行而不被迁移到其他处理器的倾向性。其实Cpus_allowed或者Cpus_allowed_list都可以看出CPU的分配。比如Cpus_allowed: 0f,这里十六进制0f转换成二进制是1111,由于最低位到最高位均为1代表系统给这个进程分派的CPU是0-3;以此类推,Cpus_allowed: 20二进制是100000,因此高位在第6位,也就是分配了CPU5。

1)再算这几个核的频率使用情况
2)进程PID所在核,通过cat /proc/$pid/task/$tid/status查看

具体计算方式可以看下面代码的注释

由于top的cpu比例是可以用来作为参考标准的,但由于top执行的时延,可能会导致top拿到的数据跟汇编拿到的数据时间上有偏移导致不准确,因此想到一个方案就是写一个死循环,让CPU每时每刻跑满,这样来验证一下数据获取的稳定性问题。 下面做了几组实验,看看实验数据,数据标示统一说明:
1)top Cpu值: 采用top命令获取的进程cpu占比(方案2)
2)当前进程cpu时间片累计:/proc/$pid/stat的Jiffies在时间段内的消耗的时间片(方案3)
3)当前cpu频率:使用汇编指令计算频率,1.612903168E9为1.613Ghz(方案1)
4)CPUWeightUsage:读取CPU各个核的当前频率来计算频率占用率(方案4)
当CPU没有占用资源开销时,TOP值执行准确,当前进程cpu时间片累计数也很低,Process CPUWeightUsage反应的是当前这个CPU核繁忙程度,因此可以看到CPU空闲率偏高。




通过这组实验可以看出进程所在CPU开销波动很大,为什么不像top值那么准呢? 其实这个是最准确的,因为我在写进程占用程序时,是用sleep来控制CPU占用,因此呈现周期性,从0%~100%才是正常的,但是TOP指令是一个时间段内均匀的数值。另一个佐证的办法就是看当前cpu频率,可以看到通过汇编取到的整机CPU频率是很高的,基本到达了最高频率,但TOP值监控不到。



在这里可以看到时间片消耗特别多,Process CPUWeightUsage跟top均能反应当前进程的性能状况


经查看,两个应用进程被系统分配到不同的CPU上,因此理论上来说,测试APP的资源占用跟实验1应该比较一致,事实上也的确如此。top跟Process CPUWeightUsage可以很好的反应当前状态。




在这个实验中,启用了一个CPU占用100%的其他应用(图中上面的进程),同时启用了测试APP(图中下面的进程)  可以看到两个进程,系统分在不同的CPU上,因此不会发生资源抢占的情况,但此时设备的性能状态是比较繁忙的。在这种情况下,top命令是检测不到的, 而CPUWeightUsage则能看出空载率不是很高,时不时会出现CPU满载的情况,但整体自身CPU影响不太大。经实测,在测试APP上也没有卡顿现象发生。




通过几组实验可以发现TOP整体来说是比较准确的,但是耗时太大,资源消耗也大;汇编指令方案并不能很好的反应当前应用APP所在CPU核心的繁忙程度,但可以反应整机CPU繁忙程度;Process CPUWeightUsage可以基本上可以实时反应当前设备以及进程所在CPU核的繁忙程度。
综合来说方案4的场景覆盖能力比较强,因此建议用方案4结合方案3一起综合评价:
1)当Process CPUWeightUsage使用率频繁在80%以上时(5次有3次),可以认定为高繁忙状态;
2)当Process CPUWeightUsage使用率频繁在50%~80%之间时,可以认定为普通状态;
3)当Process CPUWeightUsage使用率有较多次出现0%的情况或者50%以下较多时,可以多分配一些任务,CPU基本上比较空闲。
此外需要说明的是:
(1)使用CPU频率的方式来评估设备以及程序繁忙程度,只能用与以前不一样的惯性思维去做。原来CPU可能在占用40%左右,发热就很明显了,而用频率监控的方案,由于CPU自身的调节机制,频率无时无刻都在改变,因此,我们需要看CPU低频跟高频比例,当CPU处于低频状态多,那就可以多分配任务,当CPU处在高频状态多,那就少分配任务。另外还可以结合进程单位时间消耗的时间片增长率变化来评估自身APP是不是处于任务过多的状态。 (2)尽量不要以CPU绝对占用率来做任务分派,只要评估执行任务执行时间可以在较短的时间内完成,不出现死循环的情况,一般CPU都有自己的策略方案,例如interactive或者ondemand等,都会根据当前任务自动调整。 (3)有时候可能自己进程什么事情都没干,但有可能出现测试频率很高的情况,因为CPU的核不一定只跑当前应用,还有很多其他任务需要执行,尤其是当系统将应用分派到一个大核上时,这种波动上下限会更多。
后期我们会根据每个维度陆续写相关的测试文章,如果你有兴趣,请关注我们哦。

长按指纹识别图中的二维码,获取更多测试干货分享!



将我们公众号置顶

不会漏掉我们的原创干货哦!
var first_sceen__time = (+new Date());if ("" == 1 && document.getElementById('js_content')) { document.getElementById('js_content').addEventListener("selectstart",function(e){ e.preventDefault(); }); } (function(){ if (navigator.userAgent.indexOf("WindowsWechat") != -1){ var link = document.createElement('link'); var head = document.getElementsByTagName('head')[0]; link.rel = 'stylesheet'; link.type = 'text/css'; link.href = "//res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg_new/winwx45ba31.css"; head.appendChild(link); } })();
shefferliao
赞赏
长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。
阅读
分享 在看
已同步到看一看
取消 发送
我知道了
确定

已同步到看一看写下你的想法
最多200字,当前共字 发送
已发送
确定
写下你的想法...
取消
确定
最多200字,当前共字
发送中
微信扫一扫 关注该公众号
微信扫一扫 使用小程序
即将打开""小程序
取消 打开