[パフォーマンスの最適化] Perfetto を使用してアプリケーション起動パフォーマンスのボトルネックを特定する

Android アプリケーションの起動の最適化に関連する記事は多くの人が書いていますが、主に起動パフォーマンスにどのような変更が加えられたかに焦点を当てており、アプリケーションの起動パフォーマンスを分析して特定する方法について説明している記事はほとんどありません。

この記事では、Perfetto を使用した私の個人的な経験を組み合わせて、車載アプリケーションの起動時間の測定方法と、起動時間を測定した後、パフォーマンスのボトルネックをどのように分析できるかを説明します。

アプリケーションの起動パフォーマンスを分析する前に、まず Android でのアプリケーションの起動時間に関する基本的な常識を簡単に理解します。

アプリケーションの起動時間

初期表示時間(TTID)

初期表示時間 (TTID) は、システムが起動意図を受信して​​からアプリケーションがインターフェイスの最初のフレームを表示するまでの時間であり、ユーザーがアプリケーション インターフェイスを表示する時間です。

TTIDを測定する

アプリケーションが上記の作業をすべて完了すると、次のログ出力が logcat に表示されます。

/system_process I/ActivityTaskManager: Displayed xxxx/.MainActivity: +401ms

すべてのリソースが完全に読み込まれて表示されるまでの Logcat 出力に表示される時間メトリクス。レイアウト ファイルで参照されていないリソース、またはアプリがオブジェクトの初期化の一部としてリソースを作成する時間は省略されます。

logcat 出力のログには、追加のフィールド合計が含まれる場合があります。次のように:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

この場合、初回の測定は、描画された最初のアクティビティのみに対して行われます。total時間の測定はアプリの起動時から計測され、初めて起動したが画面に何も表示されない別のアクティビティが含まれる場合があります。total時間測定値は、個々のアクティビティの時間と合計起動時間に差がある場合にのみ表示されます。

一部のブログで紹介されているコマンドを使用してam start -S -W計測した時間は、実際には初期表示時間です。ほとんどの場合、初期表示時間はアプリケーションの実際の起動時間を表しません。たとえば、アプリケーションが起動するとき、ネットワークから最新のデータを同期する必要があります。実際の起動時間は、すべてのアプリケーションが起動したときにかかる時間です。これが次に紹介するTTFD「Time Total Display」です。

フル表示までの時間 (TTFD)

フル表示までの時間 (TTFD、フル表示までの時間)。これは、システムが起動インテントを受け取ってから、アプリケーションがすべてのリソースとビュー階層のロードを完了するまで、つまりユーザーが実際にアプリケーションを使用できるようになるまでの時間です。

TTFDを測定する

方法 1: 電話をかけるreportFullyDrawn()

reportFullyDrawn()アプリケーションがすべてのリソースとビュー階層のロードを完了した後にメソッドを呼び出すことができ、アプリケーションが完全に表示されたことをシステムに知らせて、完全な表示時間を計算できるようにします。このメソッドが呼び出されない場合、システムは TTID のみを計算できますが、TTFD は計算できません。

system_process I/ActivityTaskManager: Fully drawn xxxx/.MainActivity: +1s54ms

方法 2: フレームを解除する

フレーム分割法は現在、車載アプリケーションの起動時間を計算する最も一般的な方法であり、フレーム分割法にはさまざまな記録方法とフレーム分割方法があります。

Potplay60fpsに対応したカメラ(60fpsカメラに対応した携帯電話でも可)を使用してアプリケーションの起動動画を撮影し、ビデオプレーヤーを使用してデスクトップのクリックとのフレーム数の違いを確認するのが一般的です。アプリケーションの全画面表示までの時間を60で割ると、アプリケーションの起動時間が得られます。

上記の方法はテスターに​​適していますが、開発者により適した別の方法は FFmpeg フレーム分割です。

FFmpeg ダウンロード アドレス: http://ffmpeg.org/download.html?aemtn=tg-on

まず adb を使用して Android デバイスに接続し、画面録画コマンドを使用してアプリケーションの起動時にビデオを録画します。

adb shell screenrecord /sdcard/launch.mp4

FFmpegを使用してビデオのフレーム番号を確認する

ffmpeg -i launch.mp4 

ビデオ フレーム数が 60fps 未満の場合は、引き続き FFmpeg を使用してビデオにフレームを追加して 60fps にします。

ffmpeg -i launch.mp4 -filter:v fps=60 output.mp4

フレームいっぱいのビデオの各フレームを画像に分割し、デスクトップをクリックしてからアプリケーション画面が完全に表示されるまでのフレーム数の差を計算します。

ffmpeg -i output.mp4 output_%04d.jpg

または、フレームで埋められたビデオをアニメーション GIF に変換し、画像ブラウザを使用してフレームをカウントします (MAC OS の組み込み画像ブラウザで十分です)。

ffmpeg -i output.mp4 -vf fps=60,scale=320:-1:flags=lanczos -loop 0 output.gif

以上、アプリの起動時間と測定方法の紹介でしたが、詳しくはAndroidの公式ドキュメント「アプリの起動時間 | アプリの品質 | Android」に詳しく記載されていますのでご参照ください。

注目すべき点は、主流の車両アプリケーション (8155 プラットフォームを例として) の現在の平均起動時間は次のとおりであるということです。

  • コールドスタートTTFD

サードパーティの大規模インターネット アプリケーションは 2.6 秒未満に制御する必要があり、車両システム アプリケーションは 1.6 秒未満に制御する必要があります。

  • ウォームスタート TTFD

一般に、0.8 秒未満に制御する必要があります。

上記は私の個人的な経験ですが、ホストのメーカーが異なれば、パフォーマンス要件も確実に異なります。

ペルフェットの紹介

Perfettoこれは Android 10 で導入されたシステムレベルの追跡ツールで、Android、Linux、Chrome をサポートしており、 の置き換えに使用されますSystraceProfilerや と比較するとAGI、アプリケーションに限定されず、システム全体の実行状況を提供できるため、アプリケーションがシステムの安定性や流暢性に影響を与えているかどうかを確認する必要がある場合、または逆に分析するために使用できます。アプリケーションに対するシステムの影響。実行中の影響が検出された場合、Perfettoシステムレベルのトレースと分析に。

Perfetto の基本的な内容については、以前翻訳した Android の公式ビデオをご覧ください: [翻訳] 最新の Android 開発スキル - Perfetto 入門

Perfetto をすぐに始めましょう

Perfettoさまざまな使用方法がありますが、個人的にはrecord_android_traceスクリプトを使用することをお勧めします。これは、Perfettoadb を使用して Android デバイスからパフォーマンス データを収集するのに役立つ補助スクリプトです。このスクリプトは次のことを行います。

  • perfettoバイナリがデバイス上で利用可能かどうかを自動的に検出し、バイナリが利用できない場合は、GitHub からダウンロードしてデバイスにプッシュしようとします。
  • トラッキング時間、バッファ サイズ、出力ファイル パスなどのトラッキング構成パラメータを自動的に設定します。
  • トレースの完了後、コマンドが自動的に実行されperfetto、出力ファイルがコンピュータにプルされます。
  • ブラウザで出力ファイルが自動的に開き、追跡結果を表示および分析できるようになります。

Record_android_trace のダウンロード アドレス: https://raw.githubusercontent.com/google/perfetto/master/tools/record_android_trace

record_android_trace使用方法は次のとおりです。

./record_android_trace [options] [category1] [category2] ...

その中で、オプションとは、次のようなオプションのパラメーターです。

  • -o OUT_FILE: 出力ファイルのパスを指定します。指定しない場合、デフォルトは perfetto_trace.pb です。
  • -t TIME: 追跡時間を指定します。指定しない場合、デフォルトは 10 秒です。
  • -b SIZE: トレース バッファ サイズを指定します。指定しない場合、デフォルトは 32 MB です。

カテゴリは、トレースされる atrace または ftrace カテゴリです。-list を使用すると、デバイスでサポートされているトレース カテゴリを表示できます。出力は次のようになります:

link@link-PC:~/Desktop$ ./record_android_trace --list
         gfx - Graphics
       input - Input
        view - View System
     webview - WebView
          wm - Window Manager
          am - Activity Manager
          sm - Sync Manager
       audio - Audio
       video - Video
      camera - Camera
         hal - Hardware Modules
         res - Resource Loading
      dalvik - Dalvik VM
          rs - RenderScript
      bionic - Bionic C Library
       power - Power Management
          pm - Package Manager
          ss - System Server
    database - Database
     network - Network
         adb - ADB
    vibrator - Vibrator
        aidl - AIDL calls
       nnapi - NNAPI
         rro - Runtime Resource Overlay
         pdx - PDX services
       sched - CPU Scheduling
         irq - IRQ Events
         i2c - I2C Events
        freq - CPU Frequency
        idle - CPU Idle
        disk - Disk I/O
        sync - Synchronization
       workq - Kernel Workqueues
  memreclaim - Kernel Memory Reclaim
  regulators - Voltage and Current Regulators
  binder_driver - Binder Kernel driver
  binder_lock - Binder global lock trace
   pagecache - Page cache
      memory - Memory
     thermal - Thermal event
         gfx - Graphics (HAL)
         ion - ION allocation (HAL)

たとえば、sched、gfx、および view をトレースする場合、出力ファイルはtrace.perfetto-trace、トレース時間は 5 秒、バッファ サイズは 16 MB である場合、次のコマンドを実行できます。

./record_android_trace -o trace.perfetto-trace -t 5s -b 16mb sched gfx view

Perfetto は起動パフォーマンスを分析します

Perfetto を使用してアプリケーションの起動パフォーマンスを分析するのは非常に簡単です。まず、record_android_trace を使用してアプリケーションの起動データをキャプチャし、次の命令を実行します。

./record_android_trace -o trace.perfetto-trace -t 15s -b 200mb gfx input view webview wm am sm audio video camera hal res dalvik rs bionic power pm ss database network adb vibrator aidl nnapi rro pdx sched irq i2c freq idle disk sync workq memreclaim regulators binder_driver binder_lock pagecache memory gfx ion

15 秒後、record_android_trace が自動的にブラウザを開きます。1 つの列にアプリケーションの起動時間がAndroid App Startups表示されます。Android App Startups以下に示すように、表示される時間はアプリケーションのTTID、つまり初期表示時間であることに注意することが重要です

Perfetto の左側にある [Metrics] を選択し、次にandroid_startupを選択して [Run] をクリックすると、以下に示すように、アプリケーションの起動時に Perfetto がさまざまなデータの分析を自動的に支援します。

android_startup {
  startup {
    startup_id: 1
    startup_type: "warm"
    package_name: "com.xxx.xxx.weather"
    process_name: "com.xxx.xxx.weather"
    process {
      name: "com.xxx.xxx.weather"
      uid: 1000
      pid: 3376
    }
    zygote_new_process: false
    activity_hosting_process_count: 1
    event_timestamps {
      intent_received: 100680138137
      first_frame: 102167532928
    }
    to_first_frame {
      dur_ns: 1487394791
      dur_ms: 1487.394791
      main_thread_by_task_state {
        running_dur_ns: 1316606193
        runnable_dur_ns: 34121303
        uninterruptible_sleep_dur_ns: 20429636
        interruptible_sleep_dur_ns: 84415940
        uninterruptible_io_sleep_dur_ns: 12221457
        uninterruptible_non_io_sleep_dur_ns: 8208179
      }
      time_activity_manager {
        dur_ns: 16070209
        dur_ms: 16.070209
      }
      time_activity_start {
        dur_ns: 97578437
        dur_ms: 97.578437
      }
      time_activity_resume {
        dur_ns: 833413073
        dur_ms: 833.413073
      }
      time_choreographer {
        dur_ns: 481555469
        dur_ms: 481.555469
      }
      time_inflate {
        dur_ns: 1241538748
        dur_ms: 1241.538748
      }
      time_get_resources {
        dur_ns: 6173178
        dur_ms: 6.173178
      }
      time_verify_class {
        dur_ns: 1675365
        dur_ms: 1.675365
      }
      time_gc_total {
        dur_ns: 82049531
        dur_ms: 82.049531
      }
      time_dlopen_thread_main {
        dur_ns: 15522344
        dur_ms: 15.522344
      }
      time_lock_contention_thread_main {
        dur_ns: 4711976
        dur_ms: 4.711976
      }
      time_jit_thread_pool_on_cpu {
        dur_ns: 375033124
        dur_ms: 375.033124
      }
      time_gc_on_cpu {
        dur_ns: 81314427
        dur_ms: 81.314427
      }
      jit_compiled_methods: 218
      other_processes_spawned_count: 6
    }
    verify_class {
      name: "com.xxx.xxx.weather.service.VoiceActionManager"
      dur_ns: 1675365
    }
    dlopen_file: "/system/priv-app/Weather/Weather.apk!/lib/arm64-v8a/libffavc.so"
    dlopen_file: "/system/priv-app/Weather/Weather.apk!/lib/arm64-v8a/libpag.so"
    dlopen_file: "/vendor/lib64/hw/[email protected]"
    dlopen_file: "libadreno_utils.so"
    dlopen_file: "/vendor/lib64/hw/[email protected]"
    dlopen_file: "/vendor/lib64/hw/gralloc.msmnile.so"
    dlopen_file: "libadreno_app_profiles.so"
    dlopen_file: "libEGL_adreno.so"
    system_state {
      dex2oat_running: false
      installd_running: false
      broadcast_dispatched_count: 0
      broadcast_received_count: 0
      most_active_non_launch_processes: "media.codec"
      most_active_non_launch_processes: "app_process"
      most_active_non_launch_processes: "media.hwcodec"
      most_active_non_launch_processes: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
      most_active_non_launch_processes: "/system/bin/audioserver"
      installd_dur_ns: 0
      dex2oat_dur_ns: 0
    }
slow_start_reason: "GC Activity"
slow_start_reason: "Main Thread - Time spent in Running state"
slow_start_reason: "Time spent in view inflation"
  }
}

android_startupは、Android アプリケーションの起動パフォーマンスを記録および分析するために使用されるデータ構造であり、起動タイプ、起動時間、起動理由、起動依存関係、システム ステータスなど、アプリケーション起動プロセス中のさまざまな情報が含まれています。

android_startupの内容は protobuf 形式のテキストで、com.xxx.xxx.weather という名前の天気予報アプリケーションの起動データを表します。その中で最も重要なのは、最後の段落にあるslow_start_reasonで、アプリケーションの起動が遅い原因を示しており、3番目のセクションでは分析に焦点を当てます。

他のフィールドの意味は次のとおりです。

startup_id:は、これが最初の起動であることを示す一意の識別子です。

startup_type:は列挙型で、これがウォーム スタートアップであること、つまりアプリケーション プロセスはすでに存在しますが、フォアグラウンドでのアクティビティがないことを示します。

package_nameprocess_name は、アプリケーションのパッケージ名とプロセス名を表します。

プロセス:名前、ユーザー識別子 (uid)、プロセス識別子 (pid) など、アプリケーション プロセスに関する情報を表します。

zygote_new_process:新しいプロセスが zygote を通じて作成されるかどうかを示します。ここでは false です。

activity_hosting_process_count:このプロセスでホストされているアクティビティの数を示します。ここでは 1 です。

events_timestamps:さまざまなイベントのタイムスタンプを示します。たとえば、intent_received は起動意図を受信した時刻を示し、first_frame は最初のフレームが表示された時刻を示します。

to_first_frame:起動意図の受信から最初のフレームの表示までの時間と詳細を示します。これには、合計時間、メインスレッドのさまざまな状態の時間、さまざまな操作の時間、さまざまなリソースの使用量などが含まれます。

verify_class:クラス名や時刻など、クラスのロードの検証に関する情報を示します。

dlopen_file:ファイル名を含む、共有ライブラリ ファイルを開くことに関する情報を示します。

system_state: dex2oat または installd が実行されているかどうか、ブロードキャストが送信または受信されているかどうか、どの非起動プロセスが最もアクティブであるかなど、システムのステータスを示す情報。

ペルフェットの練習

起動時に GC をトリガーする

現象: 「GC Activity」がthrow_start_reasonに表示されます。これは、GC アクティビティによって起動フェーズ中のアプリケーションの起動が遅くなることを示します。

分析: [タイムラインを表示] をクリックして、Perfetto タイムライン インターフェイスに戻ります。起動タイムラインでは、アプリケーションの GC スレッドであるHeapTaskDaemonという名前のスレッドがあることがわかりますが、起動フェーズ中に約 100 ミリ秒アクティブであったため、 activityResume タイムラインが 100 ミリ秒延長されました。この現象が誤って発見されることを防ぐために、複数の測定を行った結果、アプリケーションの起動時に GC アクティビティが確実にトリガーされることがわかりました。写真が示すように:

理由: タイムラインのフォワード分析により、アプリケーションが起動フェーズで特殊なフォントを読み込むことが判明しました。フォントは約 13MB です。アプリケーション開発と連絡した結果、フォントが次の場所に移動されたことを確認しました。システム層とアプリケーション層はフォントをロードする必要はありません。フォントを削除すると、アプリケーションの起動時に GC が 100% トリガーされなくなります。

メインスレッドでの時間のかかる操作

現象: 「メイン スレッド - 実行状態で費やされた時間」がthrow_start_reasonに表示されます。これは、起動フェーズ中に、より時間のかかる操作がメイン スレッドで実行されることを意味します。

理由: この状況はアプリケーション開発中に非常に一般的です。アプリケーション開発者は自然に、メイン スレッド アクティビティの OnCreate または onStart メソッドの下にクロスプロセス データ取得操作を配置します。これらの IPC メソッドは ANR をトリガーしませんが、ANR の起動が遅くなります。アプリケーションに配置され、実行のためにスレッド プールまたはコルーチンに配置される必要があります。

OpenDexFilesFromOat には時間がかかります

現象: 「Main Thread - Time requested in OpenDexFilesFromOat*」がthrow_start_reasonに表示され、起動フェーズ中の dex ファイルの読み取りに多くの時間が費やされていることを示します。

理由: この状況は車の Android システムでより一般的です。これは、システムが起動を高速化するためにシステム内の dex2oat プロセスを変更したためにこの現象が発生した可能性がありますが、それほど時間がかからない場合は無視しても問題ありません。

ここで「可能性がある」と述べたのは、現在の車載 OS は迅速に起動するためにネイティブ Android に多くの変更が加えられており、実際の状況に基づいて詳細な分析を行う必要があるためです。

連続マルチフレーム描画タイムアウト

現象: Perfetto でのアプリケーションの起動時間は 1.3 秒程度とそれほど長くありませんが、フレーム分割方式を使用した後、アプリケーションの起動後に若干のラグが発生し、実際の起動時間が 2.1 秒に延びる場合があります。 s. Perfetto でのパフォーマンスは以下の通りで、最初のフレームを描画した後、続く 2、4、5 フレームの描画時間が 150ms を超えています。

分析: Perfetto によって提供されたフレーム描画タイムラインは、ほとんどの時間がビューのレイアウトに費やされていることを示しています。これは、最初のフレームが描画された後、複数のページの再描画がトリガーされることを示しています。

理由: コードとアプリケーション ログを組み合わせると、アプリケーションは起動時に空のデータでページを 1 回更新し、その後 IPC インターフェイスからデータを取得してページを再度更新していることがわかりました。また、コードの欠陥により、データはリフレッシュが4回連続で実行されてしまい、状況が発生します。最初のフレーム後に連続描画タイムアウトが発生しないように、欠陥のあるコードを修正します。

参考文献

https://developer.android.com/topic/performance/vitals/launch-time#time-initial

おすすめ

転載: blog.csdn.net/linkwj/article/details/132460341