2022-11-111720次浏览
0评论
3收藏
3点赞
分享
本文尝试从能找到的资料和实际现象分析安卓的自动降频导致游戏性能下降的现象。
由于时间有限,机器使用时间有限,涉及测试了好多种工具,甚至开发了一个CPUGPU模拟负载的小工具,所以更细致的测试可能需要更多人力以后继续研究。因为测试样本有限,不能作为确证,只能作为参考。
因为每个厂商都有自己的方案(包括魔改Linux核心,选择性加入电量控制Patch,自定义电量Patch,硬件魔改),由于厂商没提交测试工具和实际测试机器有限,所以本文不能涵盖所有情况,但是Android的自动降频现象是普遍存在的。这个机制也涉及电压,所以这个工作方式叫DVFS-DynamicVoltageFrequencyScaling。
本文尝试得到这种现象的共性和探讨如何规避,参考的文章重要的都在附件中,也包括开发的小工具,可以自行继续测试。
2018年的时候,Google专家过来指导,有幸参加,对优化又有了更多认识,大家可以直接翻到最后面看。
看了这么多文章,可以知道为什么Android的功耗控制如此糟糕。因为ARM,Intel,三星,一大堆公司是以合作的方式魔改Linux核心的,光是降频策略都好几种,一个IPA写了好几年。
因为IPA和EAS等,从15年开始魔改Linux核心,所以根本不用指望一个统一的策略(2017开始稳定下来了),但是后面芯片的制程一直在优化,发热会有明显的降低,功耗控制也会越来越好。2016是个分水岭,2016出的芯片功耗都好很多,基本不用太担心;我们要担心的主要的是2016之前的芯片。
APP应该尽量合理控制CPU和GPU的负载,让它不要发热就不会降频;应该尽量平衡负载,让Task平均到时间片,否则采样机制完全无法处理。另外请务必翻到最后看2018和Google交流之后,Google那边给出的建议,这些是不矛盾的,并且更深入。
1、中度负载:不管Android厂家跟你怎么说黑科技,你认为只有6成的性能可以用就行了。我们在小米5上测试,如果发热之后,比2014年的MOTOX都要卡顿。
2、平均负载:不要突然来一大段很密集的计算或者渲染,如果引起某个DVFS控制机制临时降级适配,很难升回去。
3、Shader复杂度:对发热和性能影响很大,降低Shader复杂度性价比很高。在我们另外一个分级测试里面,发现把Shader复杂度和PPI降低到一定程度,小米2A都能稳定跑很长时间,对应Messiah的Shader的LOD,原生支持。
4、PPI降低:就是锁BackBuffer渲染,Messiah原生支持。经验是260PPI~400PPI,然后当玩家机器很差的时候,如小米2A,可以降到100PPI。
5、性能分级:对CPU,GPU,做二个性能列表;关键列是CPU/GPU型号,后面是你的Shader复杂度,后处理复杂度,角色数量限制,特效数量限制等性能指标;然后根据玩家机器启动对应行就可以了。只要有足够的粒度,没有太多优化的情况下都可以让大部分机型稳定跑起来。
6、记得启动的时候杀了其他进程:申请进程管理的权限,然后游戏启动的时候关掉其它进程,大部分游戏都有这样一段小代码。非常常见的手段,但是记得做。
①升频需要20ms, 会有一个明显的延迟;
②CPU的大小核心和GPU, 3者是分开控制的;
③算法的核心是: CPU or the GPU is able to run at maximum speed whenever the thermal budget allows-在温度允许的情况下, 让CPU和GPU尽量高速;
④温度升高, 允许的最高频率下降;
⑤下图可以看到, ARM自己的测试都证明, Android核心是个不断降频的过程(下图红线):
①根据"DTO0052A_intelligent_power_allocation_white_paper", GPU负载增高, CPU会降频来适配它(在810的CPU上得到证实);
②根据EAS的描述, 旧的Android核心判断CPU的负载, 是采取间隔采样的方式, 所以可以解释升频延迟;
③看完全部文章之后, 感觉Android对游戏以帧为单位突变的负载完全无法正确应付;
④没有EAS之前的,根据采样方式, 会Miss掉突变;
有了EAS之后, 即使是可以动态分配任务了(看下面sched-DVFS), 但是游戏的负载通常不能分解为"任务",于是并没有用。
IPA是集成在Linux核心的, 都是可选Patch, 进化方式如下:
①第一代 HMP- Heterogenerouse Mult-Processing, 异体多核(就是大小核);
②第二代 IPA, 引入统一调配的功耗控制机制;
③第三代 IPA+EAS (Energy-Aware Scheduling), 引入动态的Task+Budge的分配策略, 参考sched-DVFS;
IPA从Linux Kernel4.2开始第一次正式合并进去; 根据Android网页, Android7才正式合并IPA了. 即之前的都是野蛮做法; 每个厂自己搞或者自己打IPA的Patch;
各个厂商的SOC有自己的设计, 自从高通被ARM在810坑了一代之后, 都是自己的研发的ARM指令兼容核心, 如Kyro;
降频机制不是标准的,手机的制程在优化, 不管谁设计的, 都能让功耗和温度稳定下来;
主要根据这个Energy Aware Scheduling (EAS) progress update
2015引入了 sched-DVFS, 对比野蛮的降频策略, 会根据可以运行的最高频率动态分配任务, 至少看上去可以让偷懒的CPU工作起来(下图上半部分)。
检查CPU负载的PELT模块被Intel的人重写了, 考虑了单核频率之类(上图); 但是看完感觉: 完全不对劲;
IPA会动态调配大小核心和GPU之间的功耗, 统一考虑, 而不是单独一发热就降低(说的好像本来不是这样是对的一样);
看完这堆东西, 不得不说, 搞那么复杂都是扯淡:
扯淡1: 没法统一搞定核心的散热问题, 如果核心因为温度就不能高频工作, 那么这些年堆高的核心频率都是无意义的; 事实上测试, 即使小米5, 经常也只能工作在6成最高功耗上。
扯淡2: 奥卡姆剃刀——其实只要发现是个游戏(设置了某种OpenGL Context和某种输入), 就应该用允许最高频率运行; 现在所有的策略只是在拖后腿
扯淡3: 现行大量的乱七八糟的组件, 各方魔改Linux核心, 碎片化版本的做法, 最终能达成一个基本稳定又好的方案, 需要几倍时间.
非扯淡: 当年PC体系也是好多技术N久才普及, 而苹果可以什么都上最新的, 离散化造成生命力蓬勃的生态圈同时也对新事物有阻碍, 挺正常的; 所以好的电源策略可见未来几年时间还是可以在Android生态圈搞定的
制程优化, 这个对发热影响很重要。根据:
台积电和三星在2017都会量产10nm制程的处理器;2016是个分水岭, 之后的机器相对好很多,处理器发热好不少,但是比起以前28-14的进化会少很多。
实际测试高通650的机器的发热就轻度很多, 《梦幻西游三维版》能推到极限一直维持一个比较稳定的帧数而没有之前的CPU那样要再降一个等级。
不需要看, 实在太多东西了, 本文已经整理的差不多了, 直接看本文即可; 附件里面有下载好的网页
核心最好的网页请搜索: ARM Intelligent Power Allocation(IPA); 从这里可以一层层找到相关的大部分说明
AnroidAuthority有很好的讲述; 也讲述了大小核心的工作机制
ARM’s Intelligent Power Allocation adds some more clever to thermal management 描述了很多关键做法
另外一个Android Performance Patterns;
这里有这个视频的全系; 不过是将其他部分的, 和降频没有必然关系
芯片信息
可以参考下面这个文章
上述有相当多的知识量, 直接看下面结论就行了
其实很简单, 不是降频了, 而是切到小核心继续工作; 那切到小核心, 高速缓存咋办?答案是必然造成延迟。
如果在一个瞬发的时候负载低, 被切到低核心, 那么下一个瞬发的UI绘制高负载任务岂不是有严重延迟?事实上还不如高通在801上采取的简单的4核心一起升降频的策略,
对游戏不友好是事实, 省电也是事实; 同样给那么多饭; 怠工毕竟能撑更长时间。
2018年, Android Studio 3.2之后, 很多工具变得实用了, 多了:
GAPID, Systrace, SimplePerf, Android Studio版本内置的Systrace等
之前2017的时候Systrace是很不好用的, 现在变得好用了
GAPID用来Profile GPU, 其实不比Mali Graphic Debugger和Snapdragon Profiler好用; 这几个都可以试试
Systrace, 这个是Android主打的Profiler, 可以看到每时每刻的频率, 不过事件还是要自己用宏输入
SimplePerf类似Windows的xperf, 用采样来收集信息, 新版本支持c++堆栈捕捉了, 已经相当靠谱了
用Perfmon观察频率现象--没有GPU
用Trickster MOD可以观察到GPU频率, 但是没有悬浮窗; 需要root和xposed框架, 非常麻烦
TRex测试--GFXBenchmarkGL或者3DMark, 后者在酷市场那个版本可以
Android Monitor--需要自己编译的, 不行
高通Trepn Power Profiler, 在高通机器上很好用, 可以观测到CPU和GPU
PowerVR可以用PVRMonitor, 然而用PVR芯片的安卓手机简直是熊猫
测试工具组的EPGM, 挺好用的, 但是没有GPU数据; 和工具组沟通过, 不同的芯片和厂商经常改东西, 没有GPU频率的标准获取方法
-对Android6进行负载测试
-对突变网页需求进行测试
-GPU频率的工具
-调频的那个工具做实验
-制程比较
首先得说, 比他们同厂的宕机多多Snapdragon Profiler好用了很多; 基本想要的东西都有了
首先要去Settings, 设置要要Profile的东西, 勾上Frequancy, CPU Load还有GPU Load, GPU Frequency等
然后按右上, 转为高级模式(普通预设的图不是我们想要的)
点Profile System
跑你的游戏
跑完按箭头回到Trepn Profiler, 点Stop Profile, 存csv
然后回电脑, 在/storage/emulated/0/trepn找到结果, 在PC开心的用Excel分析好了.
有的机器在'/storage/emulated/legacy/trepn'
优点非常多; 缺点很明显, 只支持到810系列 支持列表
非常好的观察性能的工具; 是Android内核自己反馈出来的数据, 比较不依赖硬件; 教程链接
安装: 用最新的AndroidStudio, 什么都选上. 手机要是Android4.1(API16)以上
USB连接 ,打开Tools > Android > Android Device Monitor. 选中一个左边的Device, 然后在左边Device上面, 找到一个无法形容的锯齿图案的奇怪按钮. (反正鼠标放上去有提示, 一个个仔细看)
如果启动失败, 尝试手工python启动:
通常涉及漏了库: py -2 -m pip install pypiwin32
py -m pip install pySerial
telemetry 要手工安装
老实说, 弄了半天有点脱离愤怒了, 至少弄个requirement.txt也会减少很多别人的工作的!!!
最后还是不行. 说要装个Linux虚拟机才可以使用. 真没见过这么诡异的工具; 其实不反对Linux才能用, 但官网难道就不应该说一声么。
测试了好多工具, 只有Trepn Profiler能同时观测到CPU和GPU, 并且能导出数据分析。然而满足Trepn Profiler, 又有大小核心设计的芯片暂时只找到810这种芯片。
做了小的apk, 可以实际强行控制cpu和gpu的负载, 原理如下:
①利用多线程强行wait构造cpu的负载。
②利用atan2和matrix_m_m等耗时的指令+循环构造gpu负载; 注意我们要制造负载而不是要制造延迟, 读取N个贴图制造的是延迟不是负载。
提升GPU到极限, 发现CPU也相应被强行降频了. (因为没法显示全, 所以只对4个核心采样)
随着时间, 有的核心脱线了; 可能是发热引起的。
这次CPU和GPU负载都调整为50%(apk里面显示), 反正就是中度烤机; GPU也没有满负载工作;
这次核心只有最后的7,8核心罢工, 其它核心坚持工作了很长时间; 依然有降频, 但是总的来说好很多.
(根据2018-10-Google专家的指导)
可以使用的工具: GAPID, Systrace, SimplePerf, Android Studio版本
其中GAPID类似Mali Graphics Debugger, 用来调试OpenGL
Systrace可以捕捉游戏事件, 分析卡顿, 比较像Intel IPA, 但是能做的事情少一些, 另外比较依赖你自己用宏告诉它事件
SimplePerf类似windows的xperf, 用sampling来捕捉, 新版本支持捕捉c++堆栈了
对上述工具, 最好用的手机, 还是Google Pixel系列
大核心, 只要有一个核心达到某个频率, 就所有核心都是那个频率
同理小核心也是, 也就是大小核心, 都会因为某个线程工作太多, 导致即使其它核没事做也浪费频率
P=C(V^2)f, 但是频率和电压是成关系的, 所以不能简单认为是线性关系或者是平方关系
根据曲线, 可以认为 Q ~= C f^2; 功耗和频率平方成正比
所以应该尽量把东西打散, 可以观察到, 单核运行在2000MHz下, 比4核运行在500MHz下功耗大了很多
核心频率超过某个阈值, 功耗达到一定程度, 就会触发Android的降频现象, 所以基于降频这个原因也应该打散任务分布式运行
要主动把主要线程都绑定到大核心, 利用 sched_setAffinity函数, StackOverflow的Code Piece:
#include <sys/syscall.h>
#include <pthread.h>
void setCurrentThreadAffinityMask(int mask)
{
int err, syscallres;
pid_t pid = gettid();
syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
if (syscallres)
{
err = errno;
LOGE("Error in the syscall setaffinity: mask=%d=0x%x err=%d=0x%x", mask, mask, err, err);
}
}
另外, Google方说, 如果有8个核心, 你就认为最后的4个是大核心就行了, 虽然没有任何文档保证这个, 但是从无例外。
不要在渲染中触发某些java接口如getRotation, 有可能让你的进程从大核心被切换到小核心, Systrace可以观测到。
根据Google专家的说法, 每次读取内存也会耗电, 因为需要对电子元件加电压. 尽量减少内存访问也可以降低电量消耗。
带宽消耗也能明显耗电和性能, 不分显存内存, 显存更明显, 譬如简单的减少读取BackBuffer能明显降低电量消耗; 这个和上面的原因是对应的. 另外因为手机的总线本来就比较低带宽, 大量的对Buffer的IO必然也会导致延迟。
Vsync可以用Swappy这个Google内部工具来处理(这个工具没有公开宣传, 还不到大规模推广阶段)。
反正注意不要弄出ANR。
评论 0