【Bugly乾貨分享】那些年我們用過的顯示性能指標
Bugly 技術乾貨系列內容主要涉及移動開發方向,是由 Bugly 邀請騰訊內部各位技術大咖,通過日常工作經驗的總結以及感悟撰寫而成,內容均屬原創,轉載請標明出處。
前言:那些年我們用過的顯示性能指標
註:Google 在自己文章中用了 Display Performance 來描述我們常說的流暢度,為了顯得有文化,本文主要用「顯示性能」一詞來代指「流暢度」(雖然兩者在概念上有細微差別)。
從 Android 誕生的那一刻起,流暢度就為眾人所關注。一時之間,似乎所有人都在討論 Android 和 iOS 誰的流暢度更好。但是,毫不誇張的說,流暢度絕對是 Android 眾多性能維度中最為奇葩的一個。因為,為了刻畫這一性能維度,業界設計了各式各樣的指標來對其進行衡量。可以說弄清了這些指標我們就明白了什麼是流暢度,可是這似乎並不太容易。
筆者簡單搜集了一些業界中提及的顯示性能指標,大家可以來品評一下:
指標名稱:FPS
相關資料:Android性能測試之fps獲取指標名稱:Aggregate frame stats(N 多個指標)
相關資料:Testing Display Performance指標名稱:Jankiness count、Max accumulated frames、Frame rate
相關資料:JankTestBase.java指標名稱:SM、Skipped frames
相關資料:Android應用性能評測調優
面對如此之多的顯示性能指標,想必大家也會跟筆者一樣,心中難免疑惑叢生。其實,我們只需要依次弄清楚以下三個哲學問題,所有的問題也許就會迎刃而解:
- 你是誰——這些指標具體反映了什麼問題
- 你從哪兒來——這些指標數值是怎麼得到的
- 你要到哪兒去——這些指標如何落地來指導優化
因此,本文將嘗試依次從上訴三個問題來逐步分析和探討各個顯示性能指標。
Step 1:你是誰——這些指標具體反映了什麼問題
總所周知,脫離了具體的應用背景,所有的指標都是沒有意義的。所以,為了徹底弄清楚各個顯示性能指標的具體身份,我們勢必得從 Android 的圖像渲染流程說起。
具體展開之前,首先需要說明的是,為了降低複雜程度和本章篇幅,在這個環節之中,我們只討論圖像渲染流程中的各個具體環節所對應的指標有哪些。而指標的具體定義,由第二章《你從哪兒來——這些指標數值是怎麼得到的》進行討論。
Android 圖像渲染流程
下圖是筆者結合各類資料(主要是是源碼及官方文檔),在根據自己的理解梳理出的幾種常見場景下的圖像渲染流程:
PS 1:筆者個人技術水平有限,若存在理解有誤的地方還望指正。
PS 2:本文主要討論的 Android 源碼為 Android 6.0備註:基於 OpenGL 的應用可以使用 Choreographer 中的 VSYNC 信號來進行圖像渲染工作的安排。
上面這幅圖涉及的概念較多,要完全吃透估計得費不少時間。不過好在我們只是想弄明白顯示性能各種指標的含義,所以我們只需要理清下面兩大關係即可:
SurfaceFlinger、HWComposer與Surface的關係
- Surface:可以理解為Android系統中的一個基本顯示單元。只要使用Android任意一種API繪圖,繪製的結果都將反映在Surface上。
- SurfaceFlinger:服務運行在System進程中,用來統一管理系統的幀緩衝區設備,其主要作用是將系統中的大部分Surface進行合成。SurfaceFlinger主要使用GPU進行Surface的合成,合成的結果將形成一個FrameBuffer。
- HWComposer:即Hardware Composer HAL,其作用是將SurfaceFlinger通過GPU合成的結果與其他Surface一起最終形成BufferQueue中的一個Buffer。此外,HWComposer可以協助SurfaceFlinger進行Surface的合成,但是否進行協助是由HWComposer決定的。
- 值得注意的是,有的Surface不由WindowManager管理,將直接作為HWComposer的輸入之一與SurfaceFlinger的輸出做最後的合成。
Choreographer、SurfaceFlinger、HWComposer與VSYNC的關係
- VSYNC:Vertical Synchronization的縮寫,它的作用是使GPU的渲染頻率與顯示器的刷新頻率(一般為固定值)同步從而避免出現畫面撕裂的現象。
- HWComposer:VSYNC信號主要由HWComposer通過硬體觸發。
- Choreographer:當收到VSYNC信號時,Choreographer將按優先順序高低依次去調用使用者通過postCallback提前設置的回調函數,它們分別是:優先順序最高的CALLBACK_INPUT、優先順序次高的CALLBACK_ANIMATION以及優先順序最低的CALLBACK_TRAVERSAL。
- SurfaceFlinger:Surface的合成操作也時基於VSYNC信號進行的。
簡單來說,Android 圖像渲染流程主要由以下特徵:
- 我們可以簡單把 Android 圖像渲染架構分為應用(Surface)、系統(SurfaceFlinger)、硬體(Screen)三個層級,其中繪製在應用層,合成及提交上屏在系統層,顯示在硬體層;
- 無論應用(Surface)、系統(SurfaceFlinger)、硬體(Screen)都是當且僅當繪製內容發生改變,才會對繪製內容進行處理;
- 系統中的 SurfaceFlinger 以及絕大部分 Surface 都是按照 VSYNC 信號的節奏來安排自己的任務;
- 目前,絕大部分 Surface 都屬於 Hardware Rendering。
各個指標在 Android 圖像渲染流程所代表的意義
大致梳理了 Android 的圖像渲染流程之後,我們需要做的一件事情,就是看看上面提到的指標,都對應了渲染流程的哪些階段,這樣對於我們了解各個指標所反映的具體物理意義及其優勢劣勢都有極大幫助。再次強調,在這個環節之中,我們的討論僅限於只討論指標所對應的渲染流程的具體階段,各指標的具體定義由第二章具體展開。
系統層級(SurfaceFlinger)的顯示性能指標
- 基礎數據:SurfaceFlinger 合成次數
- 指標意義:
- 系統合成幀率:FPS
- 特別說明:
- SurfaceFlinger 僅在顯示區域內的 Surface 有提交內容更新時才會進行合成(上屏),因此,系統合成幀率低並不一定意味著圖像顯示性能差,有可能是因為當前並沒有任何的內容更新所導致。
- 若顯示區域內的某個待測 Surface 持續進行更新時, SurfaceFlinger的合成(上屏)的頻率可以在某種程度上反映該 Surface 的顯示性能,但從理論上分析該指標並不一定準確。這是因為,若顯示區域內尚存在其他 Surface,它們也會影響 SurfaceFlinger 的合成(上屏)的行為,從而干擾結果。
- 若某個 Surface 的合成不在 SurfaceFlinger 中進行(如 Camera Preview),則該 Surface 的顯示性能無法用這類指標進行衡量。
應用層級(Surface)的顯示性能指標
- 基礎數據:繪製過程中每一幀的關鍵時間點(如開始繪製時間、結束繪製時間等)
- 指標意義:
- 應用繪製幀率:Frame rate
- 應用繪製輪詢頻率:SM
- 應用繪製超時(跳幀)的次數:Aggregate frame stats、Jankiness count、Skipped frames
- 應用繪製超時(跳幀)的幅度:Aggregate frame stats、Max accumulated frames、Skipped frames
- 特別說明:
- 與 SurfaceFlinger 類似, Surface也僅在有內容更新時才會進行繪製,因此,繪製頻率低並不一定意味著圖像顯示性能差,有可能是因為當前並沒有任何的內容更新所導致。
- 如 SM、Skipped frames 這類指標,由於其基礎數據取自 Choreographer,若 某些 Surface 的繪製不依賴於 Choreographer ,則這些指標無法衡量該 Surface 的顯示性能。
- 如 Aggregate frame stats、Jankiness count、Max accumulated frames、Frame rate 這類指標, 由於其基礎數據僅在硬體繪製(Hardware Rendering)過程中進行統計,屬於 HWUI 的功能,所以非硬體繪製的 Surface 自然無法使用這類指標進行衡量。
小結
評價顯示性能的各個指標,可以按其在圖像渲染流程中的作用,分為以下兩類:
- 系統層級的指標僅有 FPS 一根獨苗,它的限制是 Surface 的和合成需要在 SurfaceFlinger中進行;
- 應用層級的指標較多,它們之中又可以分為兩類:1) SM、Skipped frames 需要 Surface 依賴 Choreographer進行繪製,才能正常工作;2) Aggregate frame stats、Jankiness count、Max accumulated frames、Frame rate 屬於 HWUI 的功能, 需要 Surface 的繪製由 HWUI 進行才能進行分析。
Step 2:你從哪兒來——這些指標數值是怎麼得到的
第一章的內容僅僅是站在整個圖像繪製流程的高度來簡單分析各個指標的,本章將進一步分析各個指標的基礎數據來源以及具體計算方式。
基礎數據:系統層級(SurfaceFlinger)的合成(上屏)的次數
前面說到,在 Android 系統中,SurfaceFlinger 扮演了系統中所有 Surface 的管理者的角色,當應用程序所對應的 Surface 更新之後,絕大多數的 Surface 都將在 SurfaceFlinger 之中完成了合併的工作之後,最終才會在 Screen 上顯示出來。
當然, SurfaceFlinger 的執行也是由 VSYNC 信號驅動的,這也決定了每秒鐘合成次數的上限就是 60 次。當 SurfaceFlinger 接收到 Surface 更新通知的時候,將會由 SurfaceFlinger::handleMessageRefresh 函數進行處理,其中包含重建可見區域、初始化、合成等步驟。這裡,我們主要關注 SurfaceFlinger::doComposition() 這個方法。
void SurfaceFlinger::handleMessageRefresh() {n ...n if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {n // Latch buffers, but dont send anything to HWC, then signal anothern // wakeup for the next vsyncn preComposition();n repaintEverything();n } else {n preComposition();n rebuildLayerStacks();n setUpHWComposer();n doDebugFlashRegions();n doComposition(); //重點關注對象n postComposition();n }n ...n}n
在 doComposition 中,完成 Surface 的合成之後,都會調用 DisplayDevice::flip(),它會使用變數 mPageFlipCount 統計我們進行合成的次數,這個變數就是我們統計 FPS 的核心原始數據。mPageFlipCount 記錄了 SurfaceFlinger 一共進行了多少次合成,也可以簡單理解為,SurfaceFlinger 向屏幕提交了多少幀的數據。
void SurfaceFlinger::doComposition() {n ATRACE_CALL();n const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);n for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {n const sp<DisplayDevice>& hw(mDisplays[dpy]);n if (hw->isDisplayOn()) {n ...n hw->flip(hw->swapRegion);//重點關注對象n ...n }n // inform the h/w that were done compositingn hw->compositionComplete();n }n postFramebuffer();n}n
void DisplayDevice::flip(const Region& dirty) const {n ...n mPageFlipCount++;n}n
不僅如此, Android 還為我們獲取這個基礎數據提供了比較方便的方法。通過執行 adb 命令:service call SurfaceFlinger 1013,我們就可以得出當前的 mPageFlipCount。
C:Usersxiaosongluo>adb shellnshell@cancro:/ $ sunsunroot@cancro:/ # service call SurfaceFlinger 1013nservice call SurfaceFlinger 1013nResult: Parcel(00aea4f4 ....)n
FPS 的計算方法
根據 FPS 的定義,我們不難逆推得出 FPS 的計算方法:
在 t1 時刻獲取 mPageFlipCount 的數值 v1,在在 t2時刻獲取 mPageFlipCount 的數值 v2,FPS 的計算公式:
FPS = (v2 - v1) / (t2 - t1);n
需要注意的是:mPageFlipCount 的原始數據是 16 進位的,一般而言計算之前需要先進行進位轉換。
基礎數據:應用層級(Surface)的繪製過程中每一幀的關鍵時間點(FrameInfo)
請大家先注意 FrameInfo 是由 Android 6.0(具體來講是 Android M Preview) 引入到 HWUI 模塊中的統計功能。 因此,目前來講絕大多數系統上的大多數應用都暫時無法獲取這一基礎數據。不過 This IsTheFuture。
我們再來仔細瞧瞧 Google 給出的顯示性能測試的十全大補丸 《Testing Display Performance : Aggregate frame stats》 。其中,特別值得關注的是 adb shell dumpsys gfxinfo
framestats 這一條命令。通過這條命令,我們獲取每一幀繪製過程中每個關鍵節點的耗時情況,從而仔細的分析潛在的性能問題。
不得不說,按照 Google 給出的這種測試方法進行測試得到的顯示性能數據是非常全面的。
這些基礎數據都是記錄在 FrameInfo 之中,由 CanvasContext 在doFrame()時進行記錄。相關的主要源碼如下:
//源碼:FrameInfo.cppn#include "FrameInfo.h"n#include <cstring>nnnamespace android {n namespace uirenderer {n const std::string FrameInfoNames[] = {n "Flags",n "IntendedVsync",n "Vsync",n "OldestInputEvent",n "NewestInputEvent",n "HandleInputStart",n "AnimationStart",n "PerformTraversalsStart",n "DrawStart",n "SyncQueued",n "SyncStart",n "IssueDrawCommandsStart",n "SwapBuffers",n "FrameCompleted",n };nn void FrameInfo::importUiThreadInfo(int64_t* info) {n memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));n }n } /* namespace uirenderer */n} /* namespace android */n
Aggregate frame stats 指標的計算方法
首先需要說明的是 Aggregate frame stats 不是一個指標,而是一系列指標集合。我們來看一個具體的 Aggregate frame stats 的例子:
Stats since: 752958278148ns
Total frames rendered: 82189Janky frames: 35335 (42.99%)
90th percentile: 34ms95th percentile: 42ms99th percentile: 69msNumber Missed Vsync: 4706Number High input latency: 142Number Slow UI thread: 17270Number Slow bitmap uploads: 1542Number Slow draw: 23342
以上統計信息的實現可以詳見源碼:GfxMonitorImpl.java
在 Android M 以上的系統上,上述信息的獲取十分方便(事實上也只有這些系統能夠獲取這些信息)。僅需要執行以下命令即可:
adb shell dumpsys gfxinfo <PACKAGE_NAME>n
Jankiness count、Max accumulated frames、Frame rate 指標的計算方法
首先需要說明的是:Jankiness count、Max accumulated frames、Frame rate 與 Aggregate frame stats的基礎數據並不一致,它們的基礎屬於來源於 gfxinfo(Profile data in ms)。
只是在 Android M 中 gfxinfo(Profile data in ms) 的基礎數值來源於 FrameInfo,詳見源碼:FrameInfoVisualizer。但在更早的系統之上, gfxinfo(Profile data in ms) 的數值也可以獲取。
這裡需要特別指出的是, gfxinfo(Profile data in ms)只保存了 Surface 最近渲染的128幀的信息,因此,Jankiness count、Max accumulated frames、Frame rate 也僅僅是針對這 128 幀數據所計算出來的結果,它們的具體含義分別是:
- Jankiness count:根據相鄰兩幀繪製時間的差值,「估計」是否存在跳幀並進行跳幀次數的統計;
- Max accumulated frames: 根據相鄰兩幀繪製時間的差值,「估計」這 128 幀繪製過程中可能形成的最大連續跳幀數;
- Frame rate:計算所得平均(繪製)幀率。
如果你對具體的計算過程感興趣,可以參考詳見源碼:JankTestBase
基礎數據:應用層級(Surface)的繪製過程中每一幀的關鍵時間點(Choreographer)
先說一句有點繞口的話: Choreographer 是依據 Choreographer 繪製的 Surface 在 UI 繪製過程中最為核心的機制。
Choreographer 的工作機制簡單來說就是,使用者首先通過 postCallback 在 Choreographer 中設置的自己回調函數:
- CALLBACK_INPUT:優先順序最高,和輸入事件處理有關。
- CALLBACK_ANIMATION:優先順序其次,和Animation的處理有關。
- CALLBACK_TRAVERSAL:優先順序最低,和UI等控制項繪製有關。
那麼,當 Choreographer 接收到 VSYNC 信號時,Choreographer 會調用 doFrame 函數依次對上述借口進行回調,從而進行渲染。
那麼顯然,doFrame 的執行效率(次數、頻率)也就是我們需要的顯示性能數據。而這樣的基礎數據,Choreographer 自身也進行了記錄。如下面代碼中, jitterNanos 記錄了繪製前後兩幀所間隔的時間差, 而 skippedFrames 則記錄了 jitterNanos 這段時間 doFrame 錯過了多少個 VSYNC 信號,即跳過了多少幀。
// Set a limit to warn about skipped frames.n// Skipped frames imply jank.nprivate static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.choreographer.skipwarning", 30);nnvoid doFrame(long frameTimeNanos, int frame) {n ...n final long jitterNanos = startNanos - frameTimeNanos;n if (jitterNanos >= mFrameIntervalNanos) {n final long skippedFrames = jitterNanos / mFrameIntervalNanos;n if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {n Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");n }n ...n final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;n ...n frameTimeNanos = startNanos - lastFrameOffset;n }n ...n}n
上述數據的獲取並不是那麼的直接,所以需要一定的手段。方法一共有三種,都不難:
- Logcat 方案
缺點:該方案需要系統授權 「Adb Root」 許可權,用於修改系統屬性;對於丟幀信息只能統計分析,無法進行實時處理。
優點:設置完成後,可以獲取系統中所有應用各自的繪製丟幀情況(丟幀發生的時間以及連續丟幀的數量)。
其實,仔細觀察代碼,我們就可以注意到 Choreographer 源碼中本身就有輸出的方案:
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {n Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");n}n
唯一阻礙我們獲取數值的是:skippedFrames 的數值只有大於 SKIPPED_FRAME_WARNING_LIMIT 才會輸出相關的警告。而 SKIPPED_FRAME_WARNING_LIMIT 的數值可以由系統參數 debug.choreographer.skipwarning 來設定。
注意:初始條件下,系統中不存在 debug.choreographer.skipwarning 參數,因此 SKIPPED_FRAME_WARNING_LIMIT 將取默認值 30。因此,正常情況下,我們能夠看見上訴 Log 出現的機會極少。
因此,如果我們修改(設定)系統屬性 debug.choreographer.skipwarning 為 1,Logcat 中將列印出每一次丟幀的Log。需要說明的是,由於為 SKIPPED_FRAME_WARNING_LIMIT 賦值的代碼段由 Zygote 在系統啟動階段載入,而其他應用都是在拷貝復用 Zygote 中的設定,因此設定系統屬性後需要重啟 Zygote 才能使得上述設定生效。
具體的設置方法如下:
setprop debug.choreographer.skipwarning 1nsetprop ctl.restart surfaceflinger; setprop ctl.restart zygoten
設定完成以後,我們可以直接通過 Logcat 中的信息得到系統中所有應用的繪製丟幀信息,包括丟幀發生的時間以及連續丟幀的數量。不過由於 Logcat 信息的滯後性,以上信息我們幾乎只能進行在測試完成後進行統計分析,而無法進行實時處理。
- Choreographer.FrameCallback 方案
缺點:該方案需要將測試代碼與待測應用打包在一起,因此理論上僅能測試自己開發的應用。
優點:可以對丟幀信息進行實時處理
我們先來看看 Choreographer.FrameCallback 的定義。
Implement this interface to receive a callback when a new display frame is being rendered. The callback is invoked on theLooper thread to which the Choreographeris attached.
通過這個介面,我們可以在每一幀被渲染的時候記錄下它開始渲染的時間,這樣在下一幀被處理是,我們不僅可以判斷上一幀在渲染過程中是否出現掉幀,而整個過程都是實時處理的,這為我們可以及時獲取相關的調用棧信息來輔助定位潛在的性能缺陷有極大的幫助。
- 代碼注入方案
缺點:該方案需要通過注入程序為指定應用注入測試代碼,因此需要系統為注入程序授權 「應用Root」 許可權。
優點:與 Choreographer.FrameCallback 方案一致。
該方案可以簡單理解為通過注入的方式來實現與 Choreographer.FrameCallback 方案一樣的目的。因此,這裡我們主要討論兩者在實現方式上的區別。
顯而易見,我們需要注入的對象是 Choreographer ,因此理論上任何第三方應用都是可以被注入的。但是隨著 Android 系統對」應用Root」 許可權管理越來越嚴格,所以該方案可用的範圍越來越小。
SM 指標的計算方法
根據定義,SM 其實類似於 FPS,它被設計為可以衡量應用平均每秒執行 doFrame() 的次數。我們可以認為它是在衡量 Surface 渲染輪詢的次數。
針對 Logcat 方案,我們只需統計測試過程中目標進程一共掉了多少幀,由於對於絕大多數應用在沒有丟幀的情況下會針對每一次 VSYNC 信號執行一次 doFrame(),而 VSYNC 絕大多數情況下每秒會觸發 60 次,因此我們可以反向計算得出 SM 的數值:
SM = (60* totalSeconds - totalSkippedFrames) / totalSeconds;n
針對 Choreographer.FrameCallback 方案 以及 代碼注入方案,我們需要在代碼中自己進行統計輸出(可以是設計成實時的,也可以設計成測試結束後進行統計計算的)。
Skipped frames 指標的計算方法
這個指標的就是指當前應用在丟幀發生時的丟幀幀數。
針對 Logcat 方案, 該數值直接在 Logcat 中輸出,並且帶有時間信息。
04-18 16:31:24.957 I/Choreographer(24164): Skipped 4 frames! The application may be doing too much work on its main thread.n04-18 16:31:25.009 I/Choreographer(24164): Skipped 2 frames! The application may be doing too much work on its main thread.n
針對 Choreographer.FrameCallback 方案 以及 代碼注入方案,我們可能很方便的通過計算前後兩幀開始渲染的時間差獲得這一數值,同樣方便。同樣與 Logcat 方案 不同的是,它也是可以設計成實時計算的。
小結
通過對各個顯示性能指標的分析,我們可以知道,雖然目前指標眾多,但其實有本質區別的指標確很少:
- 系統層面:
- 合成(上屏)幀率:FPS
- 應用層面:
- 跳幀次數:Aggregate frame stats、Jankiness count、Skipped frames
- 跳幀幅度:Aggregate frame stats、Max accumulated frames、Skipped frames
- 繪製幀率:Frame rate
- 繪製輪詢頻率:SM
更為重要的是,我們從上述的分析中知道了各個指標都有著自己的優勢和不足,這也從根本上決定了它們各自有各自的用法。
Step 3:你要到哪兒去——這些指標如何落地來指導優化
其實指標的用法也是多種多樣的,為了方便討論,我們僅從日常監控、缺陷定位以及數據上報三個方面來討論各個顯示性能指標是如何落地的。
日常監控
- FPS:數據形式最為直觀(FPS 是最早的顯示性能指標,而且在多個平台中都有著類似的定義),且對系統平台的要求最低(API level 1),遊戲、視頻等連續繪製的應用可以考慮選用,但不適用於絕大多數非連續繪製的應用;
- SM:數據形式與 FPS 類似,可以很好的彌補 FPS 無法準確刻畫非連續繪製的應用顯示性能的缺陷;
- Aggregate frame stats:除了對系統平台有較高的要求以外,其採集方式最為簡單(系統自帶功能);
- Skipped frames:與 Aggregate frame stats 類似, 信息量相對較少,但可適用範圍更廣
特別說明:Jankiness count、Max accumulated frames、Frame rate 只統計了128幀的信息(約2~3秒),而且 Jankiness count、Max accumulated frames對於掉幀情況的計算並非是一個準確值,因此這些指標都不太適用於日常監控
舉個栗子,筆者服務的某個產品使用如下的一些指標來監控產品與競品的性能變化情況:
n測試指標n場景1n場景2n場景3n場景4nFPSn58n58n58n58nSMn59n59n59n59nNum of 6+ Skipped Framesn0n0n0n0nNum of 3+ Skipped Framesn0n0n2n0n備註:
- Num of x+ Skipped Frames 代表測試過程中發生連續丟 x 幀(及以上)的次數;
- 至於為什麼我們選擇關注連續丟 3 幀以及連續丟 6 幀的的次數,在【缺陷定位】部分有相關的分析討論;
缺陷定位
- Skipped frames:基於 Choreographer.FrameCallback 方案實現的 Skipped frames 指標,可以在卡頓出現的時刻獲取應用堆棧信息,可以在一定程度上進行缺陷定位
特別說明:
- FrameInfo 相關指標無法直接進行缺陷定位,但 FrameInfo 當中包含了大量詳盡的繪製基礎數據,對於缺陷定位也有較大幫助;
關於缺陷定位過程中連續掉幀閾值的選取,可參考維基百科中提到幾個重要的幀率數值:
- 12 fps:由於人類眼睛的特殊生理結構,如果所看畫面之幀率高於每秒約10-12幀的時候,就會認為是連貫的
- 24 fps:有聲電影的拍攝及播放幀率均為每秒24幀,對一般人而言已算可接受
- 30 fps:早期的高動態電子遊戲,幀率少於每秒30幀的話就會顯得不連貫,這是因為沒有動態模糊使流暢度降低
- 60 fps:在實際體驗中,60幀相對於30幀有著更好的體驗
以上各數據分別對應: 0 幀、1幀、2.5幀、5~6幀。(這就是為啥選擇3/6的原因)
舉個栗子, 筆者的同事萬大師(yuwan)基於上述原理自研了一款性能分析工具。該工具在集成於待測應用之後,可以自動保存如下的性能缺陷信息:
Frame lost:... (連續丟幀數量,一般我們會設定一個閾值,例如 6 幀以上我們才會進行記錄)nUser action:...(當前用戶操作)nStack trace: ...(當前堆棧信息)n
有了這個工具之後,我們可以收集應用的各個潛在「卡頓」點,用於進一步的分析和優化。
數據上報
- Aggregate frame stats:除了對系統平台有較高的要求以外,其採集方式最為簡單(系統自帶功能)、數據也比較清晰,相信基於這類指標實現性能數據上報是特別方便的
- Skipped frames :基於 Choreographer.FrameCallback 方案實現的 Skipped frames 指標,採集方式簡單,實現基礎性能數據上報、卡頓數據上報也是很方便的
這方面應用,筆者所服務的產品暫時沒有涉及,就不舉例子了。如果各位觀眾感興趣,建議參考 Android ANR 的設計理念。
小結
發現了沒有 Skipped frames 的用處很大有沒有? 而且通讀全篇,你會發現 Aggregate frame stats、Jankiness count、Max accumulated frames 這些指標都有提供類似的功能。
至於為什麼,這就不是本文需要討論的內容了,如果大家比較感興趣,筆者這裡給出兩份相關的鏈接以供各位參考:
Fps Versus Frame Time
量化和優化用戶與 Android 設備之間的交互友情附贈: 現有顯示性能指標對比
本來寫到這裡本文的主要內容就應該結束了。但是如果不對比一下顯示性能指標神馬的,總會讓人覺得缺少了一些什麼。
友情提示:下述內容相對主觀,建議各位讀者依據項目情況自行進行選擇。
n指標名稱n指標意義n基礎數據來源n採集方式n適用系統n適用應用n用途nFPSn系統合成幀率nSurfaceFlingernadb shelln略n略n監控nAggregate frame statsn應用跳幀次數、幅度nFrameInfonadb shelln最低23nHW Renderingn監控/上報nJankiness countn(估算)應用跳幀次數nFrameInfo(128幀)nadb shelln略nHW Renderingn定位nMax accumulated framesn(估算)應用跳幀幅度nFrameInfo(128幀)nadb shelln略nHW Renderingn定位nFrame raten應用繪製幀率nFrameInfo(128幀)nadb shelln略nHW Renderingn定位nSMn應用繪製輪詢頻率nChoreographern多種方式n最低16nSW/HW Rendering 及 部分 OpenGL Renderingn監控nSkipped framesn應用跳幀次數、幅度nChoreographern多種方式n最低 16nSW/HW Rendering 及 部分 OpenGL Renderingn監控/定位/上報n
更多精彩內容歡迎關注Bugly的微信公眾賬號:
騰訊 Bugly 是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合併功能幫助開發同學把每天上報的數千條Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發布後快速的了解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧…
推薦閱讀:
※實戰kotlin@android(三): 擴展變數與其它技巧
※Android L 正式版會使用思源黑體么?目前Android 4.4的默認字體是什麼?
※Android 應用開發的難點是什麼?
※一加手機好用嗎,適合學生黨嗎?
※不能拆卸電池的的安卓手機死機了怎麼辦?