close

當我們說 流暢度 的時候,我們說的是什麼?不同的人對流暢性(卡頓掉幀)有不同的理解,對卡頓閾值也有不同的感知,所以有必要在開始這個系列文章之前,先把涉及到的內容說清楚,防止出現不同的理解,也方便大家帶着問題去看這幾篇問題,下面是一些基本的說明

對手機用戶來說,卡頓包含了很多場景,比如在 滑動列表的時候掉幀、應用啟動白屏過長、點擊電源鍵亮屏慢、界面操作沒有反應然後閃退、點擊圖標沒有響應、窗口動畫不連貫、滑動不跟手、重啟手機進入桌面卡頓 等場景,這些場景跟我們開發人員所理解的卡頓還有點不一樣,開發人員會更加細分去分析這些問題,這是開發人員和用戶之間的一個認知差異,這一點在處理用戶(或者測試人員)的問題反饋的時候尤其需要注意
對開發人員來說,上面的場景包括了 流暢度(滑動列表的時候掉幀、窗口動畫不連貫、重啟手機進入桌面卡頓)、響應速度(應用啟動白屏過長、點擊電源鍵亮屏慢、滑動不跟手)、穩定性(界面操作沒有反應然後閃退、點擊圖標沒有響應)這三個大的分類。之所以這麼分類,是因為每一種分類都有不太一樣的分析方法和步驟,快速分辨問題是屬於哪一類很重要
在技術上來說,流暢度、響應速度、穩定性(ANR)這三類之所以用戶感知都是卡頓,是因為這三類問題產生的原理是一致的,都是由於主線程的 Message 在執行任務的時候超時,根據不同的超時閾值來進行劃分而已,所以要理解這些問題,需要對系統的一些基本的運行機制有一定的了解,本文會介紹一些基本的運行機制
流暢性這個系列主要是分析流暢度相關的問題,響應速度和穩定性會有專門的文章介紹,在理解了流暢性相關的內容之後,再去分析響應速度和穩定性問題會事半功倍
流暢性這個系列主要是講如何使用 Systrace (Perfetto) 工具去分析,之所以 Systrace 為切入點,是因為影響流暢度的因素很多,有 App 自身的原因、也有系統的原因。而 (Perfetto) 工具可以從一個整機運行的角度來展示問題發生的過程,方便我們去初步定位問題

Systrace 流暢性實戰目前包括下面三篇

Systrace 流暢性實戰 1 :了解卡頓原理[1]
Systrace 流暢性實戰 2 :案例分析: MIUI 桌面滑動卡頓分析[2]
Systrace 流暢性實戰 3 :卡頓分析過程中的一些疑問[3]

Systrace (Perfetto) 工具的基本使用如果還不是很熟悉,那麼需要優先去補一下 Systrace 基礎知識系列[4]

Systrace 作為分析卡頓問題的第一手工具,給開發者提供了一個從手機全局角度去看問題的方式,通過 Systrace 工具進行分析,我們可以大致確定卡頓問題的原因:是系統導致的還是應用自身的問題

當然 Systrace 作為一個工具,再進行深入的分析的時候就會有點力不從心,需要配合 TraceView + 源碼來進一步定位和解決問題,最後再使用 Systrace 進行驗證

所以本文更多地是講如何發現和分析卡頓問題,至於如何解決,就需要後續自己尋找合適的解決方案了,比如對比競品的 Systrace 表現、優化代碼邏輯、優化系統調度、優化布局等

案例說明

個人在使用小米 10 Pro 的時候,在桌面滑動這個最常用的場景裡面,總會有一種卡頓的感覺,10 Pro 是 90Hz 的屏幕,FPS 也是 90,所以一旦出現卡頓,就會有很明顯的感覺(個人對這個也比較敏感)。之前沒怎麼關注,在升級 12.5 之後,這個問題還是沒有解決,所以我想看看到底是怎麼回事

抓了 Systrace 之後分析發現,這個卡頓場景是一個非常好的案例,所以把這個例子拿出來作為流暢度的一個實戰分享

建議大家下載附件中的 Systrace,對照文章食用最佳

鑑於卡頓問題的影響因素比較多,所以在開始之前,我把本次分析所涉及的硬件、軟件版本溝通清楚,如果後續此場景有優化,此文章也不會進行修改,以文章附件中的 Systrace 為準
硬件:小米 10 Pro
軟件:MIUI 12.5.3 穩定版
小米桌面版本:RELEASE-4.21.11.2922-03151646
Systrace 分析

分析卡頓問題,我們一般的流程如下

抓取 Systrace,可以用 shell 或者手機自帶的工具來抓取
在 Chrome 中打開 Systrace 文件(html 結尾),如果不能直接打開,可以在 Chrome 中輸入 chrome://tracing/,然後把 Systrace 文件拖到裡面就可以打開
定位 App 進程在 Systrace 中的位置
定位問題發生點 - 一般以輸入事件,比如 input 事件
分析 App 進程的主線程和渲染線程
分析 SurfaceFlinger 進程的主線程和 Binder 線程
分析 SystemServer 進程的 Binder (不涉及可以不用看)

按照這個流程分析之後,需要再反過來看各個進程,把各個線索聯繫起來,推斷最有可能的原因

從 Input 事件開始

這次抓的 Systrace 我只滑動了一次,所以比較好定位,滑動的 input 事件由一個 Input Down 事件 + 若干個 Input Move 事件 + 一個 Input Up 事件組成

在 Systrace 中,SystemServer 中的 InputDispatcher 和 InputReader 線程都有體現,我們這裡主要看在 App 主線程中的體現

如上圖,App 主線程上的 deliverInputEvent 標識了應用處理 input 事件的過程,input up 之後,就進入了 Fling 階段,這部分的基礎知識可以查看下面這兩篇文章

Android Systrace 基礎知識(6) -Input解讀[5]
華為手機刷微博體驗更好?技術角度的分析和思考[6]
分析主線程

由於這次卡頓主要是鬆手之後才出現的,所以我們主要看 Input Up 之後的這段

主線程上面的 Frame 有顏色進行標註,一般有綠、黃、紅三種顏色,上面的 Systrace 裡面,沒有紅色的幀,只有綠色和黃色。那麼黃色就一定是卡頓麼?紅色就一定是卡頓麼?其實不一定,單單通過主線程,我們並不能確定是否卡頓,這個在下面會講

從主線程我們沒法確定是否發生了卡頓,我們找出了三個可疑的點,接下來我們看一下 RenderThread

分析渲染線程

放大第一個可疑點,可以看到,這一幀總耗時在 19ms, RenderThread 耗時 16ms,且 RenderThread 的 cpu 狀態都是 running(綠色),那麼這一幀這麼耗時的原因大概率是下面兩個原因導致的:

RenderThread 本身耗時,任務比較繁忙
RenderThread 的任務受 CPU 影響(可能是頻率低了、或者是跑到小核了)

由於只是可疑點,所以我們先不去看 cpu 相關的,先查看 SurfaceFlinger 進程,確定這裡有卡頓發生

分析 SurfaceFlinger

對於 Systrace 中 SurfaceFlinger 部分解讀不熟悉的可以先預習一下這篇文章 Android Systrace 基礎知識(5) -SurfaceFlinger 解讀[7]

這裡我們主要看兩個點

App 對應的 BufferQueue 的 Buffer 情況。通過這個我們可以知道在 SurfaceFlinger 端,App 是否有可用的 Buffer 提供給 SurfaceFlinger 進行合成
SurfaceFlinger 主線程的合成情況。通過查看 SurfaceFlinger 在 sf-vsync 到來的時候是否進行了合成工作,就可以判斷這一幀是否出現了卡頓。

判斷是否卡頓的標準如下

如果 SurfaceFlinger 主線程沒有合成任務,而且 App 在這一個 Vsync 周期(vsync-app)進行了正常的工作,但是對應的 App 的 BufferQueue 裡面沒有可用的 Buffer,那麼說明這一幀卡了 — 卡頓出現這種情況如下圖所示(也是上圖中第一個疑點所在的位置)

如果 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 的情況

出現這種情況主要是有下面兩個原因

RenderThread 本身耗時,任務比較繁忙
RenderThread 的任務受 CPU 影響(可能是頻率低了、或者是跑到小核了)

但是桌面滑動這個場景,負載並不高,且鬆手之後並沒有多餘的操作,View 更新之類的,本身耗時比前一幀多了將近 3 倍,可以推斷不是自身負載加重導致的耗時

那麼就需要看此時的 RenderThread 的 cpu 情況:

既然在 Running 情況,我們就去 CPU Info 區域查看這一段時間這個任務的調度情況

分析 CPU 區域的信息

查看 CPU (Kernel 區域,這部分的基礎知識可以查看 Android Systrace 基礎知識 - CPU Info 解讀[8] 和 Android Systrace 基礎知識 -- 分析 Systrace 預備知識[9])這兩篇文章

回到這個案例,我們可以看到 App 對應的 RenderThread 大部分跑在 cpu 2 和 cpu 0 上,也就是小核上(這個機型是高通驍龍 865,有四個小核+3 個大核+1 個超大核)

其此時對應的頻率也已經達到了小核的最高頻率(1.8Ghz)

且此時沒有 cpu boost 介入

那麼這裡我們猜想,之所以這一幀 RenderThread 如此耗時,是因為小核就算跑滿了,也沒法在這麼短的時間內完成任務

那麼接下來要驗證我們的猜想,需要進行下面兩個步驟

對比其他正常的幀,是否有跑在小核的。如果有且沒有出現掉幀,那麼說明我們的猜想是錯誤的
對比其他幾個異常的幀,看看掉幀的原因是否也是因為 RenderThread 任務跑到了小核導致的。如果不是,那麼就需要做其他的假設猜想

在用同樣的流程分析了後面幾個掉幀之後,我們發現

對比其他正常的幀,沒有在小核跑的,包括掉幀後的下一幀,調度器馬上把 RenderThread 擺到了大核,沒有出現連續掉幀的情況
對比其他幾個異常的幀,都是由於 RenderThread 跑到了小核,但是小核的性能不足導致 RenderThread 執行耗時,最終引起卡頓

至此,這一次的卡頓分析我們就找到了原因:RenderThread 掉到了小核

至於 RenderThread 的任務為啥跑着跑着就掉到了小核,這個跟調度器是有關係的,大小核直接的調度跟任務的負載有關係,任務從大核掉到小核、或者從小核遷移到大核,調度器這邊都是有參數和算法來控制的,所以後續的優化可能需要從這方面去入手

調整大小核遷移的閾值參數或者修改調度器算法
參考競品表現,看看競品在這個場景的性能指標,調度情況等,分析競品可能使用的策略
Triple Buffer 在這個場景發揮了什麼作用?

在 Triple-Buffer-的作用[10] 這篇文章,講到了 Triple Buffer 幾個作用

緩解掉幀
減少主線程和渲染線程等待時間
降低 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 指的是離屏幕一個左邊的距離

正常情況下,滑動的時候,offset 是:2→4→6→8→10→12
掉了一幀的情況下,滑動的 Offset 是:2→4→6→6→8→10→12 (假設 計算 8 的這一幀超時了,就會看到兩個 6 ,這是掉了一幀的情況)
像上圖裡面,如果直接扔掉了那個耗時的幀,就會出現下面這種 Offset:2→4→6→6→10→12 ,直接從 6 跳到了 10,相當於卡了 1 次,步子扯大了一次,感官上會覺得卡+跳躍
系列文章
Systrace 流暢性實戰 1 :了解卡頓原理[11]
Systrace 流暢性實戰 2 :案例分析: MIUI 桌面滑動卡頓分析[12]
Systrace 流暢性實戰 3 :卡頓分析過程中的一些疑問[13]
附件

附件已經上傳到了 Github 上,可以自行下載:https://github.com/Gracker/SystraceForBlog/tree/master/Android_Systrace_Smooth_In_Action[14]

xiaomi_launcher.zip : 桌面滑動卡頓的 Systrace 文件,這次案例主要是分析這個 Systrace 文件
xiaomi_launcher_scroll_all_the_time.zip : 桌面一直按着滑動的 Systrace 文件
oppo_launcher_scroll.zip :對比文件
往期文章
Retrofit 是如何支持協程的
Android11外部存儲權限適配指南及方案
Android自定義實現彈幕的兩種方式及性能對比
很任性!我開發了一款自己用的天氣預報 app
Android 組件化架構下模塊自加載方案

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()