好不容易有个不那么忙的周日,本来兴致勃勃的想更新一下博客,结果睡醒照常一打开虎扑,发现男篮世界杯日本爆了大冷门赢了芬兰🇫🇮。。。 再想想前两天中国男篮四十二分惨败塞尔维亚,心里顿时超级不是滋味。作为一个篮球热爱者和国家队球迷,真心觉得难受,眼看着世界,乃至亚洲范围内的篮球水平都在逐步提高,好像只有中国男篮一直在原地踏步。
PS: 看到李凯儿, 一个臂展起码210的身材面对一个射手博格丹做低手上篮被盖真的有点无语。。。。
于是这下彻底失去了写博客的激情,恨不得马上去健身房做几组深蹲才解气。
直到睡了个午觉休息了一会,我才慢慢缓过来,life goes on。想想或许我不能为中国篮球贡献什么,那起码写点技术博客和文章,如果能帮助到任何人,也算是是对社会做出了点牛毛般的贡献,总比我坐在家打游戏难受一天强。。。
Ok,借着这个平台输出了不少私货,我们聊正题!
性能分析,提高?怎么办?
其实每次看到领导要求做性能提高或者优化我都菊花一紧,紧张的不行。因为基本上性能优化是超级难做, 而且很难出成果的活。其他人不停的发新功能,疯狂吃RAM,轮到我就是帮忙做优化,说白了就是帮别人收拾烂摊子。。。。硬件就这么个水平,每次版本更新都加新功能,那怎么会不卡??????
这谁顶得住,太难办了。浩南哥说。
所以很多性能优化最后出的数据都是必须有一定先决条件才成立的。比如用户在同时打开某几个app的时候,内存减少xx百分比,或者用户设备在radio关闭时等等。都是在某一些特殊的用例下才能解决。
或者是开发者对某个app的业务逻辑非常了解,直接在业务逻辑上做减法,比如本来启动的时候需要初始化 A B C,现在为了减少app的IO操作删除B C 把他们挪到app开启之后在做等等。
这种优化一般都需要开发者对代码非常熟悉,因为这样更容易定位代码,而且数据提高上会更好看,比较直接。
但是安卓app或者系统的开发方法论已经很成熟了,一些很低级的错误大家都很少犯了。大部分很容易定位的错误在代码审查的时候就可以发现。针对某个线程某一个代码能做的优化真的太少了。做优化经常需要好几个进程一起做分析。
性能分析,盲人摸象?
很多情况下,做优化的人不一定懂业务逻辑代码,至少不是百分百懂。至少在很多做系统的大厂,性能优化一般都是很多个不同组的人集合到一起做的。安卓开发到现在,每个小模块都牵涉好几个团队开发,如果要针对某一个用户需求做优化,可能需要至少三个团队一起,但是彼此也都不太熟悉对方代码。
这种情况下,之前说的方法论就不太管用了。我们需要对一个用户需求做全局的系统级别分析。
我们可以结合之前说过的Perfetto 工具说说怎么看系统全局的性能分析。通过查看CPU的运行情况看看到底为什么这么慢。
从哪里来到哪里去
再分析之前,开发者至少需要确定你要查看的用户需求从什么时候开始到什么时候结束。Perfetto的默认设置是不保存Log文件的,没有log肯定是没法确定时间的。这里我们需要加上Log的filter设置,告诉perfetto你需要查看哪些log。
保存好之后,运行获得trace,你可以看到你的Perfetto UI下面出现了日志,最关键的是,这些日志可以直接被定位到CPU运行的时间线上
你还可以在对应的时间线上插上一个小旗子!
在确定好日志在时间线上的位置之后,开发者可以专注于这个时间片段内发生的事情了!
CPU啥情况?
打开Perfetto UI之后,最上面一行就是CPU的执行情况。就像github的提交情况一样,颜色越深,越绿,代表CPU越繁忙。
我抄好绿!!!
比如下面这个图中
选中的这1.5秒内,CPU简直绿的发指!后面就慢慢的变白了,说明这1.5秒内CPU基本上是满负荷的运行,如果在这个过程中启动进程线程过多,那就会出现cpu starvation。 大家都想跑东西,但是都分配不到CPU cycle。这种情况下速度必然会变慢。
哪些进程有毛病?
Perfetto可以记录CPU在任意时刻分配给了哪些进程。上图这一栏就记录了密密麻麻的运行记录。每个小颜色块代表了同一个进程。
放大看就可以看出端倪。
system server 在选中的这一段中分配了好多cpu cycle!
如果你把鼠标悬浮在上面,你还可以看到某一个进程在时间段中的占比
System server 真的跑了好多!!!
进程具体跑了多久?
用鼠标把选中的CPU区域包起来,Perfetto会自动统计该时间段所有进程的运行状态
你可以很清楚的看到System server 在1.5秒内分配了快900ms的CPU时间,是整个系统中分到的大头。
进程具体跑了多久?
在确定可疑的进程之后,可以聚焦该进程,查看进程中的哪些线程在占用CPU
Binder 相关的线程占用173ms! 考虑跟进system server具体创建了哪些binder,是不是有些进程不需要bind?减少Binder 释放更多CPU cycle?
选定了时间段之后,也可以按照线程来排序,这样更方便分辨同一个进程中不同的线程运行状态。
果然这个binder 线程好像有点太占CPU了?
通过上面一些查看CPU运行情况的技巧,我们基本上可以分析出在这段时间内哪些进程占CPU的大头。减少不需要的进程或者线程的运行,可以释放更多CPU cycle 给必须的业务逻辑线程或者进程。同时需要注意的是,进程线程越多,需要的内存肯定也越多,当内存经常性的超过极限就会出现更多的swap,这时任何的操作都会变慢,延迟会呈现雪崩式的上升。
自动化
其实以上的分析对于一个复杂的系统来说还是略显不准确。比如在我这次分析里面,系统进程还有JIT 相关的任务,而且还相当活跃。
这说明我在测试的时候可能刚刚刷完系统,这样的测试随机性太大。JIT相关的线程可能会大量占用CPU cycle不说,还会伴随着很多垃圾回收任务。这对性能考究不公平。
我们需要有一套自动化的系统,做持续不断的自动化测试,同时统计某个用户需求的延迟,还有CPU运行情况的数据 (中位数?最差情况 ?最好情况?等等)。Perfetto提供相关数据的SQL query,也就是说咱不需要每次都打开UI 工具手动点击时间段分析,这样是无法自动化的。具体的SQL怎么写官网都有这里就不细说了。
比如,每次在开发者提交了一个可能会影响到性能的代码之后,CI系统需要跑这个需求N次,统计以上涉及到的数据和之前进行对比,看看有没有明显的性能倒退现象。
甚至在你做性那分析之前,就需要找好对照组进行实验(比如某个版本和某个版本之间的对比),做到有的放矢,毕竟快慢都是相对的。
可以说自动化和对照组,是性能分析最最关键的部分了。只有找出了问题的真正所在,才能谈技术上怎么解决。
- 自动化测试框架和实验室条件
- Perfetto SQL query
- 实验对照组
以上三点几乎缺一不可。
最后
写完这篇文章的时间是北京时间六点半,距离男篮vs南苏丹还有几个小时,衷心祝愿中国男篮可以和我这篇文章的主旨一样,性能再优化一些。干赢南苏丹挺进奥运啊!!!!!!
周琦,李凯儿你们两个硬一点啊!