那些年咱們用過的顯示性能指標

前言

注:Google 在本身文章中用了 Display Performance 來描述咱們常說的流暢度,爲了顯得有文化,本文主要用「顯示性能」一詞來代指「流暢度」(雖然二者在概念上有細微差異)。html

從 Android 誕生的那一刻起,流暢度就爲衆人所關注。一時之間,彷佛全部人都在討論 Android 和 iOS 誰的流暢度更好。可是,絕不誇張的說,流暢度絕對是 Android 衆多性能維度中最爲奇葩的一個。由於,爲了刻畫這一性能維度,業界設計了各式各樣的指標來對其進行衡量。能夠說弄清了這些指標咱們就明白了什麼是流暢度,但是這彷佛並不太容易。java

筆者簡單蒐集了一些業界中說起的顯示性能指標,你們能夠來品評一下:android

指標名稱:FPS
相關資料Android性能測試之fps獲取shell

指標名稱:Aggregate frame stats(N 多個指標)
相關資料Testing Display Performance架構

指標名稱:Jankiness count、Max accumulated frames、Frame rate
相關資料JankTestBase.javaapp

指標名稱:SM、Skipped frames
相關資料Android應用性能評測調優函數

面對如此之多的顯示性能指標,想必你們也會跟筆者同樣,心中不免疑惑叢生。其實,咱們只須要依次弄清楚如下三個哲學問題,全部的問題也許就會迎刃而解:工具

  • 你是誰——這些指標具體反映了什麼問題oop

  • 你從哪兒來——這些指標數值是怎麼獲得的post

  • 你要到哪兒去——這些指標如何落地來指導優化

所以,本文將嘗試依次從上訴三個問題來逐步分析和探討各個顯示性能指標。

Step 1:你是誰——這些指標具體反映了什麼問題

總所周知,脫離了具體的應用背景,全部的指標都是沒有意義的。因此,爲了完全弄清楚各個顯示性能指標的具體身份,咱們勢必得從 Android 的圖像渲染流程提及。

具體展開以前,首先須要說明的是,爲了下降複雜程度和本章篇幅,在這個環節之中,咱們只討論圖像渲染流程中的各個具體環節所對應的指標有哪些。而指標的具體定義,由第二章《你從哪兒來——這些指標數值是怎麼獲得的》進行討論。

Android 圖像渲染流程

下圖是筆者結合各種資料(主要是是源碼及官方文檔),在根據本身的理解梳理出的幾種常見場景下的圖像渲染流程:

PS 1:筆者我的技術水平有限,若存在理解有誤的地方還望指正。
PS 2:本文主要討論的 Android 源碼爲 Android 6.0

Android Graphics Pipeline

備註:基於 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 圖像渲染流程主要由如下特徵:

  1. 咱們能夠簡單把 Android 圖像渲染架構分爲應用(Surface)、系統(SurfaceFlinger)、硬件(Screen)三個層級,其中繪製在應用層,合成及提交上屏在系統層,顯示在硬件層;

  2. 不管應用(Surface)、系統(SurfaceFlinger)、硬件(Screen)都是當且僅當繪製內容發生改變,纔會對繪製內容進行處理;

  3. 系統中的 SurfaceFlinger 以及絕大部分 Surface 都是按照 VSYNC 信號的節奏來安排本身的任務;

  4. 目前,絕大部分 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 天然沒法使用這類指標進行衡量。

小結

評價顯示性能的各個指標,能夠按其在圖像渲染流程中的做用,分爲如下兩類:

  1. 系統層級的指標僅有 FPS 一根獨苗,它的限制是 Surface 的和合成須要在 SurfaceFlinger中進行;

  2. 應用層級的指標較多,它們之中又能夠分爲兩類:

    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() {
  ...
  if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
    // Latch buffers, but don't send anything to HWC, then signal another
    // wakeup for the next vsync
    preComposition();
    repaintEverything();
  } else {
    preComposition();
    rebuildLayerStacks();
    setUpHWComposer();
    doDebugFlashRegions();
    doComposition(); //重點關注對象
    postComposition();
  }
  ...
}

在 doComposition 中,完成 Surface 的合成以後,都會調用 DisplayDevice::flip(),它會使用變量 mPageFlipCount 統計咱們進行合成的次數,這個變量就是咱們統計 FPS 的核心原始數據。mPageFlipCount 記錄了 SurfaceFlinger 一共進行了多少次合成,也能夠簡單理解爲,SurfaceFlinger 向屏幕提交了多少幀的數據。

void SurfaceFlinger::doComposition() {
  ATRACE_CALL();
  const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
  for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
    const sp<DisplayDevice>& hw(mDisplays[dpy]);
    if (hw->isDisplayOn()) {
      ...
      hw->flip(hw->swapRegion);//重點關注對象
      ...
    }
    // inform the h/w that we're done compositing
    hw->compositionComplete();
  }
  postFramebuffer();
}
void DisplayDevice::flip(const Region& dirty) const {
  ...
  mPageFlipCount++;
}

不只如此, Android 還爲咱們獲取這個基礎數據提供了比較方便的方法。經過執行 adb 命令:service call SurfaceFlinger 1013,咱們就能夠得出當前的 mPageFlipCount。

C:\Users\xiaosongluo>adb shell
shell@cancro:/ $ su
su
root@cancro:/ # service call SurfaceFlinger 1013
service call SurfaceFlinger 1013
Result: Parcel(00aea4f4    '....')

FPS 的計算方法

根據 FPS 的定義,咱們不難逆推得出 FPS 的計算方法:

在 t1 時刻獲取 mPageFlipCount 的數值 v1,在在 t2時刻獲取 mPageFlipCount 的數值 v2,FPS 的計算公式:

FPS = (v2 - v1) / (t2 - t1);

須要注意的是: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 <PACKAGE_NAME> framestats 這一條命令。經過這條命令,咱們獲取每一幀繪製過程當中每一個關鍵節點的耗時狀況,從而仔細的分析潛在的性能問題。

不得不說,按照 Google 給出的這種測試方法進行測試獲得的顯示性能數據是很是全面的。

這些基礎數據都是記錄在 FrameInfo 之中,由 CanvasContext 在doFrame()時進行記錄。相關的主要源碼以下:

//源碼:FrameInfo.cpp
#include "FrameInfo.h"
#include <cstring>

namespace android {
  namespace uirenderer {
    const std::string FrameInfoNames[] = {
      "Flags",
      "IntendedVsync",
      "Vsync",
      "OldestInputEvent",
      "NewestInputEvent",
      "HandleInputStart",
      "AnimationStart",
      "PerformTraversalsStart",
      "DrawStart",
      "SyncQueued",
      "SyncStart",
      "IssueDrawCommandsStart",
      "SwapBuffers",
      "FrameCompleted",
    };

    void FrameInfo::importUiThreadInfo(int64_t* info) {
      memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
    }
  } /* namespace uirenderer */
} /* namespace android */

Aggregate frame stats 指標的計算方法

首先須要說明的是 Aggregate frame stats 不是一個指標,而是一系列指標集合。咱們來看一個具體的 Aggregate frame stats 的例子:

Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706
Number High input latency: 142
Number Slow UI thread: 17270
Number Slow bitmap uploads: 1542
Number Slow draw: 23342

以上統計信息的實現能夠詳見源碼:GfxMonitorImpl.java

在 Android M 以上的系統上,上述信息的獲取十分方便(事實上也只有這些系統可以獲取這些信息)。僅須要執行如下命令便可:

adb shell dumpsys gfxinfo <PACKAGE_NAME>

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.
// Skipped frames imply jank.
private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.choreographer.skipwarning", 30);

void doFrame(long frameTimeNanos, int frame) {
  ...
  final long jitterNanos = startNanos - frameTimeNanos;
  if (jitterNanos >= mFrameIntervalNanos) {
    final long skippedFrames = jitterNanos / mFrameIntervalNanos;
    if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
      Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");
    }
    ...
    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
    ...
    frameTimeNanos = startNanos - lastFrameOffset;
  }
  ...
}

上述數據的獲取並非那麼的直接,因此須要必定的手段。方法一共有三種,都不難:

1. Logcat 方案

缺點:該方案須要系統受權 「Adb Root」 權限,用於修改系統屬性;對於丟幀信息只能統計分析,沒法進行實時處理。
優勢:設置完成後,能夠獲取系統中全部應用各自的繪製丟幀狀況(丟幀發生的時間以及連續丟幀的數量)。

其實,仔細觀察代碼,咱們就能夠注意到 Choreographer 源碼中自己就有輸出的方案:

if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
  Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");
}

惟一阻礙咱們獲取數值的是: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 1
setprop ctl.restart surfaceflinger; setprop ctl.restart zygote

設定完成之後,咱們能夠直接經過 Logcat 中的信息獲得系統中全部應用的繪製丟幀信息,包括丟幀發生的時間以及連續丟幀的數量。不過因爲 Logcat 信息的滯後性,以上信息咱們幾乎只能進行在測試完成後進行統計分析,而沒法進行實時處理。

2. 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.

經過這個接口,咱們能夠在每一幀被渲染的時候記錄下它開始渲染的時間,這樣在下一幀被處理是,咱們不只能夠判斷上一幀在渲染過程當中是否出現掉幀,而整個過程都是實時處理的,這爲咱們能夠及時獲取相關的調用棧信息來輔助定位潛在的性能缺陷有極大的幫助。

3. 代碼注入方案

缺點:該方案須要經過注入程序爲指定應用注入測試代碼,所以須要系統爲注入程序受權 "應用Root」 權限。
優勢:與 Choreographer.FrameCallback 方案一致。

該方案能夠簡單理解爲經過注入的方式來實現與 Choreographer.FrameCallback 方案同樣的目的。所以,這裏咱們主要討論二者在實現方式上的區別。

顯而易見,咱們須要注入的對象是 Choreographer ,所以理論上任何第三方應用都是能夠被注入的。可是隨着 Android 系統對"應用Root」 權限管理愈來愈嚴格,因此該方案可用的範圍愈來愈小。

SM 指標的計算方法

根據定義,SM 其實相似於 FPS,它被設計爲能夠衡量應用平均每秒執行 doFrame() 的次數。咱們能夠認爲它是在衡量 Surface 渲染輪詢的次數。

針對 Logcat 方案,咱們只需統計測試過程當中目標進程一共掉了多少幀,因爲對於絕大多數應用在沒有丟幀的狀況下會針對每一次 VSYNC 信號執行一次 doFrame(),而 VSYNC 絕大多數狀況下每秒會觸發 60 次,所以咱們能夠反向計算得出 SM 的數值:

SM = (60* totalSeconds - totalSkippedFrames) / totalSeconds;

針對 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.
04-18 16:31:25.009 I/Choreographer(24164): Skipped 2 frames!  The application may be doing too much work on its main thread.

針對 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對於掉幀狀況的計算並不是是一個準確值,所以這些指標都不太適用於平常監控

舉個栗子,筆者服務的某個產品使用以下的一些指標來監控產品與競品的性能變化狀況:

測試指標 場景1 場景2 場景3 場景4
FPS 58 58 58 58
SM 59 59 59 59
Num of 6+ Skipped Frames 0 0 0 0
Num of 3+ Skipped Frames 0 0 2 0

備註:

  1. Num of x+ Skipped Frames 表明測試過程當中發生連續丟 x 幀(及以上)的次數;

  2. 至於爲何咱們選擇關注連續丟 3 幀以及連續丟 6 幀的的次數,在【缺陷定位】部分有相關的分析討論;

缺陷定位

Skipped frames:基於 Choreographer.FrameCallback 方案實現的 Skipped frames 指標,能夠在卡頓出現的時刻獲取應用堆棧信息,能夠在必定程度上進行缺陷定位

特別說明:

  1. FrameInfo 相關指標沒法直接進行缺陷定位,但 FrameInfo 當中包含了大量詳盡的繪製基礎數據,對於缺陷定位也有較大幫助;

  2. 關於缺陷定位過程當中連續掉幀閾值的選取,可參考維基百科中提到幾個重要的幀率數值:

    • 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 幀以上咱們纔會進行記錄)
User action:...(當前用戶操做)
Stack trace: ...(當前堆棧信息)

有了這個工具以後,咱們能夠收集應用的各個潛在「卡頓」點,用於進一步的分析和優化。

數據上報

  • 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 設備之間的交互

友情附贈: 現有顯示性能指標對比

原本寫到這裏本文的主要內容就應該結束了。可是若是不對比一下顯示性能指標神馬的,總會讓人以爲缺乏了一些什麼。

友情提示:下述內容相對主觀,建議各位讀者依據項目狀況自行進行選擇。

指標名稱 指標意義 基礎數據來源 採集方式 適用系統 適用應用 用途
FPS 系統合成幀率 SurfaceFlinger adb shell 監控
Aggregate frame stats 應用跳幀次數、幅度 FrameInfo adb shell 最低23 HW Rendering 監控/上報
Jankiness count (估算)應用跳幀次數 FrameInfo(128幀) adb shell HW Rendering 定位
Max accumulated frames (估算)應用跳幀幅度 FrameInfo(128幀) adb shell HW Rendering 定位
Frame rate 應用繪製幀率 FrameInfo(128幀) adb shell HW Rendering 定位
SM 應用繪製輪詢頻率 Choreographer 多種方式 最低16 SW/HW Rendering 及 部分 OpenGL Rendering 監控
Skipped frames 應用跳幀次數、幅度 Choreographer 多種方式 最低 16 SW/HW Rendering 及 部分 OpenGL Rendering 監控/定位/上報

騰訊 Bugly 是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧…

相關文章
相關標籤/搜索