當咱們說 流暢度 的時候,咱們說的是什麼?不一樣的人對流暢性(卡頓掉幀)有不一樣的理解,對卡頓閾值也有不一樣的感知,因此有必要在開始這個系列文章以前,先把涉及到的內容說清楚,防止出現不一樣的理解,也方便你們帶着問題去看這幾篇問題,下面是一些基本的說明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…
一我的能夠走的更快 , 一羣人能夠走的更遠