其实我们知道,在手机里面,除了相机和整机性能,还有一个用户最关键的地方:功耗。
功耗一直是用户在意的地方,如果用户用手机发现耗电耗得飞起,那估计在网上能把人喷死。
所以这里,抛砖引玉,放一个数字耳机播放音乐功耗高的问题解析,仅供学习参考。
【前提条件】飞行模式,默认音量,手动息屏
【操作步骤】
1.手机插入数字耳机,关闭音效,调节到默认媒体音量
2.打开音乐播放器,添加《测试专用音乐.mp3》到播放列表
3.点击播放歌曲2秒内手动息屏,播放45秒后记录播放2分钟的平均电流
音效开关位置:设置——声音与振动——全局音效(此处不同手机音效名字可能不一样)
【实际结果】测试版本S比R对比版本大32.4mA
【期望结果】测试版本与对比版本相差在3mA之内
这里解释一下,同一台手机,在从Android R升级Android S,升级之后,S版本上数字耳机播放音乐场景,比原先在Android R上多了32mA。
因为都是用的同一台手机,所以硬件是一样的配置,功耗可以排除由硬件引起。
使用功耗板进行功耗数据拆解:
一些无关紧要的数据我就打码了,避免麻烦。
上图是功耗拆解图,左边是Android R的数据,右边是Android S的数据,从拆解出来的功耗可以明显看出,功耗主要差异在BB上,大核(PROCB)20mA,小核(PROCL)30mA,对比正常Android R版本,大核(PROCB)5mA,小核(PROCL)8mA。
进一步抓取trace分析到底是CPU上哪个线程导致功耗异常,抓取trace分析如下图:
发现trace里有一个线程运行的时间最长:mtk.mp3.decoder,有较大的嫌疑,怀疑是decoder吃掉了功耗。
和负责编解码的同事沟通,MTK在Android S上确实引入了C2 解码框架,但是这个decoder主要是跑在小核上,功耗拆解出来,大核功耗差异有15mA,小核功耗差异有22mA,理论上C2负载的增加不会引入32mA这么多。
为了进一步测试功耗是否由C2编解码器引起的,做了一个实验,将MP3文件转换为WAV格式去播放,这样可以避免音乐文件进行解码。
对比测试,播放WAV和 MP3功耗差异在20mA 左右,并没有发现功耗有明显下降,可以确定多出来的功耗应该不是 decoder 造成的(对于硬解码负载增加功耗增益, MTK做过1080P30fps 视频播放, C2架构比OMX最多大5ma)。
但至少得出,播放MP3音乐比播放WAV格式音乐会多出10mA左右功耗,但不是本题的主要根因。
再次和测试沟通,发现同场景下,模拟耳机没有功耗问题,只有数字耳机存在问题。
这个就比较有意思了,因为在音频服务层framework代码里,是不区分模拟耳机和数字耳机的,它俩的播放流程是一致的!
再次抓取数字耳机场景下Android S和Android R两份systrace对比结果(左边S右边R):
Mix线程数字耳机S比R开销大:
extractor线程数字耳机S比R开销大:
音乐播放器APP数字耳机开销S比R大,同时也存在更多线程:
灭屏后台播放音乐, 数字耳机S上systemUI还在吃掉性能。
system server 数字耳机S比R多出11个线程。
surfaceflinger开销数字耳机S大于R。
从systrace上看,Audioserver确实没有新增负载,和之前想的一样,而且AudioserverS上负载还低于R。
目前两份systrace可以确认到:
问题搞到这里,我是觉得比较奇怪的,因为Android S和Android R都是用的同一个音乐APP进行测试,不应该这部分会引起负载增加才对。
最终确认结果如下:
怀疑上一份systrace抓的是不是有点问题,重新又抓了S和R两份systrace做对比:
可以看出Mix线程的开销S是和R差不多的,总体来看hal里S是比R开销要低,理论上功耗不会变高,还会变低才对。
此时到这里又觉得奇怪了,功耗拆解出来表示功耗差异主要由CPU引起,主要是在上层,(底层代码一般只做HW enable,是硬件功耗,不是CPU功耗),而且两次抓的systrace差异非常大,很不稳定,cpu loading表现完全不同。但总loading显示S负载还要低于R,所以功耗差异不应该在CPU上才对,这和功耗拆解出来的结论相互矛盾。
不过问题还要继续分析,当前上层基本排查完毕,开始排查底层,使用tinyplay播放音乐测试,这样可以屏蔽上层差异,tinyplay播放这样只涉及底层音频驱动,结果…
发现tinyplay播放音乐场景,Android S和Android R上数字耳机功耗差异不大,大概在2-3mA左右,基本排除底层驱动和硬件原因。
此时上层和底层都排查完毕,还是没发现问题出在哪里…
基于硬件提供的功耗拆解,耗电差异主要在CPU上,结合之前说过感觉systrace抓的有点问题,决定还是继续抓取systrace分析。
这次使用脚本和adb命令来抓取systrace,放到chrome://tracing/去分析!
多次试验然后抓取systrace发现,对比线程之间消耗差异不大,没有发现某个线程开销比较大。
不过从systrace可以看出:
Android R上线程消耗比较平均的分布在CPU0~CPU7上。
Android S线程主要分布CPU0 ~ CPU4上,CPU5~CPU7基本处于没跑什么东西。
这就比较耐人寻味了。
进一步实验发现,在Android S上,每次刷机完成之后,只要尽快息屏进行功耗测试,那么功耗就是正常的,功耗和Android R上一致,不管放多久只有不亮屏都是正常的,只要手机一从息屏变为亮屏且解锁之后,再次测试,功耗就变得异常了,也就是会比Android R上多出32mA:
关键数据就打码了,避免麻烦。可看到,息屏亮屏之后测试,确实比之前测出的功耗高出一截。
难道和亮灭屏有啥联系???不过话又说回来,为啥模拟耳机没有这个问题?
再放两张电流图:
上图是Android S上未息屏亮屏前,数字耳机播放音乐的电流图,此时功耗是正常的。
上图是Android S上息屏亮屏之后,数字耳机播放音乐的电流图。此时功耗出现异常。
为了避免麻烦,数据都打码了,可以对比两张图,发现正常时,功耗的底电流在刻度6那个地方,异常时,功耗底电流飙升到了快刻度7那个位置。
说明可能并不是因为数字耳机播放流程里某个线程导致功耗异常,而是系统功耗就出现了异常,导致底电流升高,怪不得查看了好多遍systrace也没发现啥头绪。
结合之前systrace里面,发现CPU loading状态的异常,MTK给出了查看CPU频率的指令:
cat /proc/cpufreq/MT_CPU_DVFS_CCI/cpufreq_freq
结果发现,正常功耗时,CPU频率大概在800M左右,功耗异常时,CPU频率直接飙到了1.6G。而且一直降不下来…
问题分析到这,慢慢脱离audio的范畴了,已经不是我一个小小的音频工程师能cover的了,需要专门负责内核的同事介入了。
和内核老哥们说了这个事情,结果他们又双叒叕和我说有补丁!
原来他们前天修复了一个问题,超大核拉住不释放的问题。我打上这个补丁之后,功耗果然正常了…
好家伙,就我搁这分析了好几星期,结果还不是audio的锅,又给人擦屁股了,窝不响丸了!!!