由于Android 8.0以后Google的权限限制,SDK再也拿不到进程CPU的实时占用率,只能拿到自己本身进程的Jiffies,而由于拿不到系统整体Jiffies的情况下,就没办法衡量CPU当前的消耗状况了,也没办法根据当前CPU状态实时做一些策略调整。因此进行深入研究以后,给出Android 8.0以后判断CPU状态的几个参考方案(非标准答案)。
方案1 - 通过单位时间汇编指令数获取CPU频率
(1)基础概念:
1)Jiffies 全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。
2)Tick HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒。
(2)原理:
由于我们可以控制一条汇编指令,在任意型号CPU上是都是在一个时钟周期内完成的(原子化操作),比如:
因此,在这里每一次CPU数据采样都是传循环参数10000次,也就是执行add指令130*10000次,然后我们循环执行20轮,计算出执行130*10000指令花费的最小一轮(130*10000条指令)时间min_time_s
每一条指令消耗的时间为:
而cpu频率则是cost的倒数,也就是单位时间(s)内执行cycles的数量
执行130*10000次
方案2 - 通过在代码内部执行TOP获取CPU占比
我们可以看看top命令的源码
可以看到TOP本身也是拿到/proc/stat后统计的。在这里我有一个问题也没弄明白,就是在代码内部我有权限执行top命令,此时top命令是当前进程fork出来的,应该理论上是继承了父进程的权限才对,但实际上父进程没有权限访问/proc/stat。所以本想把top的具体实现挪出来,目前只能直接驱动top命令。因此这种方案应用于实时性以及性能开销要求不高的监控程序。
方案3 - 直接看/proc/$pid/stat的Jiffies值
这种方案使用场景比较受限,用来判断当前进程的性能消耗比较准确。但是如果该设备别的应用程序导致CPU占比很高,但是自己程序的Jiffies值很小,就误以为设备不繁忙然后开了一堆线程过去,那可能设备就会挂掉。具体可以看后续的实验,在这里也给出一段参考代码:
方案4 - 读取CPU各个核的当前频率
由于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)
实验1 - 进程内部CPU开销很小时(0%)
当CPU没有占用资源开销时,TOP值执行准确,当前进程cpu时间片累计数也很低,Process CPUWeightUsage反应的是当前这个CPU核繁忙程度,因此可以看到CPU空闲率偏高。
实验2 - 进程内部CPU开销偏大时(40%)
通过这组实验可以看出进程所在CPU开销波动很大,为什么不像top值那么准呢? 其实这个是最准确的,因为我在写进程占用程序时,是用sleep来控制CPU占用,因此呈现周期性,从0%~100%才是正常的,但是TOP指令是一个时间段内均匀的数值。另一个佐证的办法就是看当前cpu频率,可以看到通过汇编取到的整机CPU频率是很高的,基本到达了最高频率,但TOP值监控不到。
实验3 - 进程内部CPU开销很大时(100%)
在这里可以看到时间片消耗特别多,Process CPUWeightUsage跟top均能反应当前进程的性能状况
实验4 - 该设备其他应用进程CPU正常开销时(15%)
经查看,两个应用进程被系统分配到不同的CPU上,因此理论上来说,测试APP的资源占用跟实验1应该比较一致,事实上也的确如此。top跟Process CPUWeightUsage可以很好的反应当前状态。
实验5 - 该设备其他应用进程CPU开销很大时(100%)
在这个实验中,启用了一个CPU占用100%的其他应用(图中上面的进程),同时启用了测试APP(图中下面的进程) ![IMAGE](docs/205D7EBDF0FBF84AF267950EDD1231B2.jpg =1697x137) 可以看到两个进程,系统分在不同的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字,当前共字
发送中
微信扫一扫 关注该公众号
微信扫一扫 使用小程序
即将打开""小程序
取消 打开