当咱们说 流畅度 的时候,咱们说的是什么?不一样的人对流畅性(卡顿掉帧)有不一样的理解,对卡顿阈值也有不一样的感知,因此有必要在开始这个系列文章以前,先把涉及到的内容说清楚,防止出现不一样的理解,也方便你们带着问题去看这几篇问题,下面是一些基本的说明html
Systrace 流畅性实战目前包括下面三篇android
Systrace (Perfetto) 工具的基本使用若是还不是很熟悉,那么须要优先去补一下 Systrace 基础知识系列git
Systrace 做为分析卡顿问题的第一手工具,给开发者提供了一个从手机全局角度去看问题的方式,经过 Systrace 工具进行分析,咱们能够大体肯定卡顿问题的缘由:是系统致使的仍是应用自身的问题github
固然 Systrace 做为一个工具,再进行深刻的分析的时候就会有点力不从心,须要配合 TraceView + 源码来进一步定位和解决问题,最后再使用 Systrace 进行验证算法
因此本文更多地是讲如何发现和分析卡顿问题,至于如何解决,就须要后续本身寻找合适的解决方案了,好比对比竞品的 Systrace 表现、优化代码逻辑、优化系统调度、优化布局等chrome
我的在使用小米 10 Pro 的时候,在桌面滑动这个最经常使用的场景里面,总会有一种卡顿的感受,10 Pro 是 90Hz 的屏幕,FPS 也是 90,因此一旦出现卡顿,就会有很明显的感受(我的对这个也比较敏感)。以前没怎么关注,在升级 12.5 以后,这个问题仍是没有解决,因此我想看看究竟是怎么回事shell
抓了 Systrace 以后分析发现,这个卡顿场景是一个很是好的案例,因此把这个例子拿出来做为流畅度的一个实战分享性能优化
建议你们下载附件中的 Systrace,对照文章食用最佳markdown
- 鉴于卡顿问题的影响因素比较多,因此在开始以前,我把本次分析所涉及的硬件、软件版本沟通清楚,若是后续此场景有优化,此文章也不会进行修改,以文章附件中的 Systrace 为准
- 硬件:小米 10 Pro
- 软件:MIUI 12.5.3 稳定版
- 小米桌面版本:RELEASE-4.21.11.2922-03151646
分析卡顿问题,咱们通常的流程以下app
按照这个流程分析以后,须要再反过来看各个进程,把各个线索联系起来,推断最有可能的缘由
此次抓的 Systrace 我只滑动了一次,因此比较好定位,滑动的 input 事件由一个 Input Down 事件 + 若干个 Input Move 事件 + 一个 Input Up 事件组成
在 Systrace 中,SystemServer 中的 InputDispatcher 和 InputReader 线程都有体现,咱们这里主要看在 App 主线程中的体现
如上图,App 主线程上的 deliverInputEvent 标识了应用处理 input 事件的过程,input up 以后,就进入了 Fling 阶段,这部分的基础知识能够查看下面这两篇文章
因为此次卡顿主要是松手以后才出现的,因此咱们主要看 Input Up 以后的这段
主线程上面的 Frame 有颜色进行标注,通常有绿、黄、红三种颜色,上面的 Systrace 里面,没有红色的帧,只有绿色和黄色。那么黄色就必定是卡顿么?红色就必定是卡顿么?其实不必定,单单经过主线程,咱们并不能肯定是否卡顿,这个在下面会讲
从主线程咱们无法肯定是否发生了卡顿,咱们找出了三个可疑的点,接下来咱们看一下 RenderThread
放大第一个可疑点,能够看到,这一帧总耗时在 19ms, RenderThread 耗时 16ms,且 RenderThread 的 cpu 状态都是 running(绿色),那么这一帧这么耗时的缘由大几率是下面两个缘由致使的:
因为只是可疑点,因此咱们先不去看 cpu 相关的,先查看 SurfaceFlinger 进程,肯定这里有卡顿发生
对于 Systrace 中 SurfaceFlinger 部分解读不熟悉的能够先预习一下这篇文章 www.androidperformance.com/2020/02/14/…
这里咱们主要看两个点
判断是否卡顿的标准以下
若是 SurfaceFlinger 进行了合成,并且 App 在这一个 Vsync 周期(vsync-app)进行了正常的工做,可是对应的 App 的 BufferQueue 里面没有可用的 Buffer,那么这一帧也是卡了,之因此 SurfaceFlinger 会正常合成,是由于有其余的 App 提供了可用来合成的 Buffer — 卡顿出现 这种状况以下图所示(也在附件的 Systrace 里面)
若是 SurfaceFlinger 进行了合成,并且 App 在这一个 Vsync 周期(vsync-app)进行了正常的工做,并且对应的 App 的 BufferQueue 里面有可用的 Buffer,那么这一帧就会正常合成,此时没有卡顿出现 — 正常状况 正常状况以下,做为对比仍是贴上来方便你们对比
回到本例的第一个疑点的地方,咱们经过 SurfaceFlinger 端的分析,发现这一帧确实是掉了,缘由是 App 没有准备好可用的 Buffer 供 SurfaceFlinger 来合成,那么接下来就须要看为何这一帧 App 没有可用的 Buffer 给到 SurfaceFlinger
上面咱们分析这一帧所对应的 MainThread + RenderThread 耗时在 19ms,且 RenderThread 耗时就在 16ms,那么咱们来看 RenderThread 的状况
出现这种状况主要是有下面两个缘由
可是桌面滑动这个场景,负载并不高,且松手以后并无多余的操做,View 更新之类的,自己耗时比前一帧多了将近 3 倍,能够推断不是自身负载加剧致使的耗时
那么就须要看此时的 RenderThread 的 cpu 状况:
既然在 Running 状况,咱们就去 CPU Info 区域查看这一段时间这个任务的调度状况
查看 CPU (Kernel 区域,这部分的基础知识能够查看 Android Systrace 基础知识 - CPU Info 解读 和 Android Systrace 基础知识 -- 分析 Systrace 预备知识)这两篇文章
回到这个案例,咱们能够看到 App 对应的 RenderThread 大部分跑在 cpu 2 和 cpu 0 上,也就是小核上(这个机型是高通骁龙 865,有四个小核+3 个大核+1 个超大核)
其此时对应的频率也已经达到了小核的最高频率(1.8Ghz)
且此时没有 cpu boost 介入
那么这里咱们猜测,之因此这一帧 RenderThread 如此耗时,是由于小核就算跑满了,也无法在这么短的时间内完成任务
那么接下来要验证咱们的猜测,须要进行下面两个步骤
在用一样的流程分析了后面几个掉帧以后,咱们发现
至此,这一次的卡顿分析咱们就找到了缘由:RenderThread 掉到了小核
至于 RenderThread 的任务为啥跑着跑着就掉到了小核,这个跟调度器是有关系的,大小核直接的调度跟任务的负载有关系,任务从大核掉到小核、或者从小核迁移到大核,调度器这边都是有参数和算法来控制的,因此后续的优化可能须要从这方面去入手
在 Triple-Buffer-的做用 这篇文章,讲到了 Triple Buffer 几个做用
那么在桌面滑动卡顿这个案例里面,Triple Buffer 发挥了什么做用呢?结论是:有的场景没有发挥做用,反而有反作用,致使卡顿现象更明显,下面是分析流程
能够看文章中 Triple Buffer 缓解掉帧 的原理:
在分析小米桌面滑动卡顿这个案例的时候,我发如今有一个问题,小米桌面对应的 App 的 BufferQueue,有时候会出现可用 Buffer 从 2 →0 ,这至关于直接把一个 Buffer 给抛弃掉了,以下图所示
这样的话,若是在后续的桌面 Fling 过程当中,又出现了一次 RenderThread 耗时,那么就会以卡顿的形式直接体现出来,这样也就失去了 Triple Buffer 的缓解掉帧的做用了
下图能够看到,因为丢弃了一个 Buffer,致使再一次出现 RenderThread 耗时的时候,表现依然是无 Buffer 可用,出现掉帧
仔细看前面这段丢弃 Buffer 的逻辑,也很容易想到,这里自己就已经丢了一帧了,还把这个耗时帧所对应的 Buffer 给丢弃了(也可能丢弃的是第二帧),不论是哪一种状况,滑动时候的每一帧的内容都是计算好的(参考 List Fling 的计算过程),若是把其中一帧丢了,再加上自己 SurfaceFlinger 卡的那一下,卡顿感会很是明显
举个例子,以滑动为例,offset 指的是离屏幕一个左边的距离
2→4→6→8→10→12
2→4→6→6→8→10→12
(假设 计算 8 的这一帧超时了,就会看到两个 6 ,这是掉了一帧的状况)2→4→6→6→10→12
,直接从 6 跳到了 10,至关于卡了 1 次,步子扯大了一次,感官上会以为卡+跳跃附件已经上传到了 Github 上,能够自行下载:github.com/Gracker/Sys…
一我的能够走的更快 , 一群人能够走的更远