Systrace 流畅性实战 2 :案例分析 - MIUI 桌面滑动卡顿分析

当咱们说 流畅度 的时候,咱们说的是什么?不一样的人对流畅性(卡顿掉帧)有不一样的理解,对卡顿阈值也有不一样的感知,因此有必要在开始这个系列文章以前,先把涉及到的内容说清楚,防止出现不一样的理解,也方便你们带着问题去看这几篇问题,下面是一些基本的说明html

  1. 对手机用户来讲,卡顿包含了不少场景,好比在 滑动列表的时候掉帧应用启动白屏过长点击电源键亮屏慢界面操做没有反应而后闪退点击图标没有响应窗口动画不连贯、滑动不跟手、重启手机进入桌面卡顿 等场景,这些场景跟咱们开发人员所理解的卡顿还有点不同,开发人员会更加细分去分析这些问题,这是开发人员和用户之间的一个认知差别,这一点在处理用户(或者测试人员)的问题反馈的时候尤为须要注意
  2. 对开发人员来讲,上面的场景包括了 流畅度(滑动列表的时候掉帧、窗口动画不连贯、重启手机进入桌面卡顿)、响应速度(应用启动白屏过长、点击电源键亮屏慢、滑动不跟手)、稳定性(界面操做没有反应而后闪退、点击图标没有响应)这三个大的分类。之因此这么分类,是由于每一种分类都有不太同样的分析方法和步骤,快速分辨问题是属于哪一类很重要
  3. 在技术上来讲,流畅度、响应速度、稳定性(ANR)这三类之因此用户感知都是卡顿,是由于这三类问题产生的原理是一致的,都是因为主线程的 Message 在执行任务的时候超时,根据不一样的超时阈值来进行划分而已,因此要理解这些问题,须要对系统的一些基本的运行机制有必定的了解,本文会介绍一些基本的运行机制
  4. 流畅性这个系列主要是分析流畅度相关的问题,响应速度和稳定性会有专门的文章介绍,在理解了流畅性相关的内容以后,再去分析响应速度和稳定性问题会事半功倍
  5. 流畅性这个系列主要是讲如何使用 Systrace (Perfetto) 工具去分析,之因此 Systrace 为切入点,是由于影响流畅度的因素不少,有 App 自身的缘由、也有系统的缘由。而 (Perfetto) 工具能够从一个整机运行的角度来展现问题发生的过程,方便咱们去初步定位问题

Systrace 流畅性实战目前包括下面三篇android

  1. Systrace 流畅性实战 1 :了解卡顿原理
  2. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
  3. Systrace 流畅性实战 3 :卡顿分析过程当中的一些疑问

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

  1. 鉴于卡顿问题的影响因素比较多,因此在开始以前,我把本次分析所涉及的硬件、软件版本沟通清楚,若是后续此场景有优化,此文章也不会进行修改,以文章附件中的 Systrace 为准
  2. 硬件:小米 10 Pro
  3. 软件:MIUI 12.5.3 稳定版
  4. 小米桌面版本:RELEASE-4.21.11.2922-03151646

Systrace 分析

分析卡顿问题,咱们通常的流程以下app

  1. 抓取 Systrace,能够用 shell 或者手机自带的工具来抓取
  2. 在 Chrome 中打开 Systrace 文件(html 结尾),若是不能直接打开,能够在 Chrome 中输入 chrome://tracing/,而后把 Systrace 文件拖到里面就能够打开
  3. 定位 App 进程在 Systrace 中的位置
  4. 定位问题发生点 - 通常以输入事件,好比 input 事件
  5. 分析 App 进程的主线程和渲染线程
  6. 分析 SurfaceFlinger 进程的主线程和 Binder 线程
  7. 分析 SystemServer 进程的 Binder (不涉及能够不用看)

按照这个流程分析以后,须要再反过来看各个进程,把各个线索联系起来,推断最有可能的缘由

从 Input 事件开始

此次抓的 Systrace 我只滑动了一次,因此比较好定位,滑动的 input 事件由一个 Input Down 事件 + 若干个 Input Move 事件 + 一个 Input Up 事件组成

在 Systrace 中,SystemServer 中的 InputDispatcher 和 InputReader 线程都有体现,咱们这里主要看在 App 主线程中的体现

如上图,App 主线程上的 deliverInputEvent 标识了应用处理 input 事件的过程,input up 以后,就进入了 Fling 阶段,这部分的基础知识能够查看下面这两篇文章

  1. www.androidperformance.com/2019/11/04/…
  2. www.androidperformance.com/2020/08/20/…

分析主线程

因为此次卡顿主要是松手以后才出现的,因此咱们主要看 Input Up 以后的这段

主线程上面的 Frame 有颜色进行标注,通常有绿、黄、红三种颜色,上面的 Systrace 里面,没有红色的帧,只有绿色和黄色。那么黄色就必定是卡顿么?红色就必定是卡顿么?其实不必定,单单经过主线程,咱们并不能肯定是否卡顿,这个在下面会讲

从主线程咱们无法肯定是否发生了卡顿,咱们找出了三个可疑的点,接下来咱们看一下 RenderThread

分析渲染线程

放大第一个可疑点,能够看到,这一帧总耗时在 19ms, RenderThread 耗时 16ms,且 RenderThread 的 cpu 状态都是 running(绿色),那么这一帧这么耗时的缘由大几率是下面两个缘由致使的:

  1. RenderThread 自己耗时,任务比较繁忙
  2. RenderThread 的任务受 CPU 影响(多是频率低了、或者是跑到小核了)

因为只是可疑点,因此咱们先不去看 cpu 相关的,先查看 SurfaceFlinger 进程,肯定这里有卡顿发生

分析 SurfaceFlinger

对于 Systrace 中 SurfaceFlinger 部分解读不熟悉的能够先预习一下这篇文章 www.androidperformance.com/2020/02/14/…

这里咱们主要看两个点

  1. App 对应的 BufferQueue 的 Buffer 状况。经过这个咱们能够知道在 SurfaceFlinger 端,App 是否有可用的 Buffer 提供给 SurfaceFlinger 进行合成
  2. SurfaceFlinger 主线程的合成状况。经过查看 SurfaceFlinger 在 sf-vsync 到来的时候是否进行了合成工做,就能够判断这一帧是否出现了卡顿。

判断是否卡顿的标准以下

  1. 若是 SurfaceFlinger 主线程没有合成任务,并且 App 在这一个 Vsync 周期(vsync-app)进行了正常的工做,可是对应的 App 的 BufferQueue 里面没有可用的 Buffer,那么说明这一帧卡了 — 卡顿出现 这种状况以下图所示(也是上图中第一个疑点所在的位置)
  1. 若是 SurfaceFlinger 进行了合成,并且 App 在这一个 Vsync 周期(vsync-app)进行了正常的工做,可是对应的 App 的 BufferQueue 里面没有可用的 Buffer,那么这一帧也是卡了,之因此 SurfaceFlinger 会正常合成,是由于有其余的 App 提供了可用来合成的 Buffer — 卡顿出现 这种状况以下图所示(也在附件的 Systrace 里面)

  2. 若是 SurfaceFlinger 进行了合成,并且 App 在这一个 Vsync 周期(vsync-app)进行了正常的工做,并且对应的 App 的 BufferQueue 里面有可用的 Buffer,那么这一帧就会正常合成,此时没有卡顿出现 — 正常状况 正常状况以下,做为对比仍是贴上来方便你们对比

回到本例的第一个疑点的地方,咱们经过 SurfaceFlinger 端的分析,发现这一帧确实是掉了,缘由是 App 没有准备好可用的 Buffer 供 SurfaceFlinger 来合成,那么接下来就须要看为何这一帧 App 没有可用的 Buffer 给到 SurfaceFlinger

回到渲染线程

上面咱们分析这一帧所对应的 MainThread + RenderThread 耗时在 19ms,且 RenderThread 耗时就在 16ms,那么咱们来看 RenderThread 的状况

出现这种状况主要是有下面两个缘由

  1. RenderThread 自己耗时,任务比较繁忙
  2. RenderThread 的任务受 CPU 影响(多是频率低了、或者是跑到小核了)

可是桌面滑动这个场景,负载并不高,且松手以后并无多余的操做,View 更新之类的,自己耗时比前一帧多了将近 3 倍,能够推断不是自身负载加剧致使的耗时

那么就须要看此时的 RenderThread 的 cpu 状况:

既然在 Running 状况,咱们就去 CPU Info 区域查看这一段时间这个任务的调度状况

分析 CPU 区域的信息

查看 CPU (Kernel 区域,这部分的基础知识能够查看 Android Systrace 基础知识 - CPU Info 解读Android Systrace 基础知识 -- 分析 Systrace 预备知识)这两篇文章

回到这个案例,咱们能够看到 App 对应的 RenderThread 大部分跑在 cpu 2 和 cpu 0 上,也就是小核上(这个机型是高通骁龙 865,有四个小核+3 个大核+1 个超大核)

其此时对应的频率也已经达到了小核的最高频率(1.8Ghz)

且此时没有 cpu boost 介入

那么这里咱们猜测,之因此这一帧 RenderThread 如此耗时,是由于小核就算跑满了,也无法在这么短的时间内完成任务

那么接下来要验证咱们的猜测,须要进行下面两个步骤

  1. 对比其余正常的帧,是否有跑在小核的。若是有且没有出现掉帧,那么说明咱们的猜测是错误的
  2. 对比其余几个异常的帧,看看掉帧的缘由是否也是由于 RenderThread 任务跑到了小核致使的。若是不是,那么就须要作其余的假设猜测

在用一样的流程分析了后面几个掉帧以后,咱们发现

  1. 对比其余正常的帧,没有在小核跑的,包括掉帧后的下一帧,调度器立刻把 RenderThread 摆到了大核,没有出现连续掉帧的状况
  2. 对比其余几个异常的帧,都是因为 RenderThread 跑到了小核,可是小核的性能不足致使 RenderThread 执行耗时,最终引发卡顿

至此,这一次的卡顿分析咱们就找到了缘由:RenderThread 掉到了小核

至于 RenderThread 的任务为啥跑着跑着就掉到了小核,这个跟调度器是有关系的,大小核直接的调度跟任务的负载有关系,任务从大核掉到小核、或者从小核迁移到大核,调度器这边都是有参数和算法来控制的,因此后续的优化可能须要从这方面去入手

  1. 调整大小核迁移的阈值参数或者修改调度器算法
  2. 参考竞品表现,看看竞品在这个场景的性能指标,调度状况等,分析竞品可能使用的策略

Triple Buffer 在这个场景发挥了什么做用?

Triple-Buffer-的做用 这篇文章,讲到了 Triple Buffer 几个做用

  1. 缓解掉帧
  2. 减小主线程和渲染线程等待时间
  3. 下降 GPU 和 SurfaceFlinger 瓶颈

那么在桌面滑动卡顿这个案例里面,Triple Buffer 发挥了什么做用呢?结论是:有的场景没有发挥做用,反而有反作用,致使卡顿现象更明显,下面是分析流程

能够看文章中 Triple Buffer 缓解掉帧 的原理:

在分析小米桌面滑动卡顿这个案例的时候,我发如今有一个问题,小米桌面对应的 App 的 BufferQueue,有时候会出现可用 Buffer 从 2 →0 ,这至关于直接把一个 Buffer 给抛弃掉了,以下图所示

这样的话,若是在后续的桌面 Fling 过程当中,又出现了一次 RenderThread 耗时,那么就会以卡顿的形式直接体现出来,这样也就失去了 Triple Buffer 的缓解掉帧的做用了

下图能够看到,因为丢弃了一个 Buffer,致使再一次出现 RenderThread 耗时的时候,表现依然是无 Buffer 可用,出现掉帧

仔细看前面这段丢弃 Buffer 的逻辑,也很容易想到,这里自己就已经丢了一帧了,还把这个耗时帧所对应的 Buffer 给丢弃了(也可能丢弃的是第二帧),不论是哪一种状况,滑动时候的每一帧的内容都是计算好的(参考 List Fling 的计算过程),若是把其中一帧丢了,再加上自己 SurfaceFlinger 卡的那一下,卡顿感会很是明显

举个例子,以滑动为例,offset 指的是离屏幕一个左边的距离

  1. 正常状况下,滑动的时候,offset 是:2→4→6→8→10→12
  2. 掉了一帧的状况下,滑动的 Offset 是:2→4→6→6→8→10→12 (假设 计算 8 的这一帧超时了,就会看到两个 6 ,这是掉了一帧的状况)
  3. 像上图里面,若是直接扔掉了那个耗时的帧,就会出现下面这种 Offset:2→4→6→6→10→12 ,直接从 6 跳到了 10,至关于卡了 1 次,步子扯大了一次,感官上会以为卡+跳跃

系列文章

  1. Systrace 流畅性实战 1 :了解卡顿原理
  2. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
  3. Systrace 流畅性实战 3 :卡顿分析过程当中的一些疑问

附件

附件已经上传到了 Github 上,能够自行下载:github.com/Gracker/Sys…

  1. xiaomi_launcher.zip : 桌面滑动卡顿的 Systrace 文件,此次案例主要是分析这个 Systrace 文件
  2. xiaomi_launcher_scroll_all_the_time.zip : 桌面一直按着滑动的 Systrace 文件
  3. oppo_launcher_scroll.zip :对比文件

关于我 && 博客

  1. 关于我 , 很是但愿和你们一块儿交流 , 共同进步 .
  2. 博客内容导航
  3. 优秀博客文章记录 - Android 性能优化必知必会

一我的能够走的更快 , 一群人能够走的更远