アプリ パフォーマンスの測定の概要

このドキュメントでは、アプリの主なパフォーマンスの問題を特定して修正する方法について説明します。

主なパフォーマンスの問題

アプリのパフォーマンス低下の原因となる問題は数多くありますが、アプリで注意すべき一般的な問題としては、次のようなものが挙げられます。

起動レイテンシ

起動レイテンシは、アプリアイコン、通知、その他のエントリ ポイントをタップしてから、ユーザーのデータが画面に表示されるまでにかかる時間です。

アプリで次の起動目標を目指します。

  • 500 ミリ秒未満でコールド スタート。コールド スタートは、起動中のアプリがシステムのメモリに存在しない場合に発生します。これは、再起動後、あるいはユーザーまたはシステムによるアプリプロセスの強制終了後、最初にアプリを起動したときに発生します。

    一方、ウォーム スタートはアプリがすでにバックグラウンドで実行されているときに発生します。コールド スタートでは、ストレージからすべてを読み込んでアプリを初期化する必要があるため、システムの負荷が最も多くなります。コールド スタートにかかる時間は、500 ミリ秒以下を目指してください。

  • レイテンシの中央値に非常に近い P95 レイテンシおよび P99 レイテンシ。アプリの起動に時間がかかると、ユーザー エクスペリエンスが低下します。アプリ起動のクリティカル パスにプロセス間通信(IPC)や不要な I/O があると、ロック競合が発生し、不整合が生じることがあります。

スクロール ジャンク

「ジャンク」という用語は、システムが要求された頻度(60 Hz 以上)で画面に描画するフレームを時間内にビルド、提供できない場合に発生する視覚的な中断を表します。ジャンクはスクロール時に最も顕著に現れ、スムーズにアニメーション化されるべきフローで中断が発生します。アプリがコンテンツをレンダリングするためにかかる時間がシステム上のフレーム継続期間よりも長いため、1 つ以上のフレームの途中で動きが一時停止し、ジャンクが現れます。

アプリは 90 Hz のリフレッシュ レートをターゲットにする必要があります。従来のレンダリング レートは 60 Hz ですが、新しいデバイスの多くは、スクロールなどのユーザー操作中に 90 Hz モードで動作します。一部のデバイスでは、さらに高いレート(120 Hz まで)がサポートされています。

ある時点でデバイスが使用しているリフレッシュ レートを確認するには、[開発者向けオプション] > [リフレッシュ レートの表示]([デバッグ] セクション内)を使用してオーバーレイを有効にします。

スムーズでない遷移

タブの切り替えや新しいアクティビティの読み込みなどの操作中に発生します。このタイプの遷移は、滑らかなアニメーションでなければならず、遅延や視覚的なちらつきを含んではいけません。

電力の非効率性

負荷がかかると電力が消費されます。不要な負荷がかかるとバッテリー駆動時間が短くなります。

コードに新しいオブジェクトを作成することで発生するメモリ割り当てが原因で、システムに大きな負荷がかかることがあります。これは、割り当て自体が Android ランタイム(ART)の労力を必要とするためだけでなく、後でそのオブジェクトを解放する場合(「ガベージ コレクション」)に時間と労力が必要になるためでもあります。割り当てとコレクションはどちらも、特に一時オブジェクトについては、以前に比べてはるかに高速かつ効率的です。以前は、可能な限りオブジェクトの割り当てを避けることがベスト プラクティスでしたが、アプリとアーキテクチャに最も適したものにすることをおすすめします。ART の能力を考えると、コードの管理が困難になるリスクを冒してまで割り当てを控えることはおすすめしません。

ただし、労力が必要になるため、内部ループで多くのオブジェクトを割り当てると、パフォーマンスの問題を引き起こす可能性があることに留意してください。

問題を特定する

パフォーマンスの問題を特定して修正するために、次のワークフローをおすすめします。

  1. 以下のクリティカル ユーザー ジャーニーを特定して検査します。
    • 一般的な起動フロー(ランチャーや通知を含む)。
    • ユーザーがデータをスクロールする画面。
    • 画面間の遷移。
    • ナビゲーションや音楽の再生のような長時間実行フロー。
  2. 次のデバッグツールを使用して、フローで何が起こっているかを検査します。
    • Perfetto: 正確なタイミング データを使用して、デバイス全体で何が起こっているかを確認できます。
    • Memory Profiler: 発生しているメモリ割り当てを確認できます 発生します。
    • Simpleperf: 特定の期間にどの関数呼び出しが最も多く CPU を使用しているかを示すフレームグラフを表示します。Systrace で時間がかかっているものを特定しても、その原因がわからなかった場合、Simpleperf が追加情報を提供します。

こうしたパフォーマンスの問題を確認してデバッグするには、個々のテスト実行を手動でデバッグすることが重要です。集計データを分析しても、前の手順を置き換えることはできません。ただし、ユーザーに実際に何が表示されているかを理解し、いつ回帰が発生する可能性があるかを特定するには、自動テストとフィールドで指標の収集をセットアップすることが重要です。

  • 起動フロー
  • ジャンク
    • フィールド指標
      • Google Play Console のフレーム指標: Google Play Console では、指標を特定のユーザー ジャーニーに絞り込むことはできません。アプリ全体のジャンクが報告されるだけです。
      • FrameMetricsAggregator を使用したカスタム測定: 特定の期間にジャンク指標を記録する FrameMetricsAggregator 説明します。
    • ラボテスト
      • Macrobenchmark を使用したスクロール
      • Macrobenchmark は、単一のユーザー ジャーニーを囲む dumpsys gfxinfo コマンドを使用してフレーム時間を収集します。これにより、特定のユーザー ジャーニーに対するジャンクの変化を把握します。RenderTime 指標は、フレームの描画にかかる時間を強調するものであり、回帰または改善の特定では、ジャンクのあるフレームの数よりも重要です。

アプリリンクは、ウェブサイトの URL に基づくディープリンクで、 ウェブサイトに属する必要があります以下は、App Link の検証が失敗する原因となる可能性があります。

  • インテント フィルタのスコープ: autoVerify を次の URL のインテント フィルタにのみ追加する 決定することもできます
  • 未確認のプロトコル スイッチ: 未確認のサーバーサイド リダイレクトとサブドメイン リダイレクト セキュリティ リスクとみなされ、検証で不合格になります。これにより、すべての autoVerify リンクが失敗します。たとえば、HTTPS リンクを検証せずに、HTTP から HTTPS にリンクをリダイレクトすると(example.com から www.example.com など)、検証が失敗する可能性があります。インテントを追加してアプリリンクを検証します。 フィルタできます。
  • 検証できないリンク: テスト目的で検証できないリンクを追加すると、システムがアプリのアプリリンクを検証しなくなる可能性があります。
  • 信頼できないサーバー: サーバーがクライアント アプリに接続できることを確認します。

パフォーマンス分析向けにアプリをセットアップする

正確で再現可能かつ実用的なベンチマークをアプリから取得するには、適切なセットアップが不可欠です。ノイズの原因を抑制しながら、できるだけ本番環境に近いシステムでテストします。以降のセクションでは、テストのセットアップを準備するための、APK とシステム固有の手順をいくつか紹介します。一部はユースケース固有のものです。

トレースポイント

アプリは、カスタム トレース イベントを使用してコードをインストルメント化できます。

トレースをキャプチャしている間、トレースにはセクションごとにわずかなオーバーヘッド(約 5 マイクロ秒)が発生するため、すべてのメソッドにトレースを適用することは避けてください。0.1 ミリ秒を超える大きな単位の処理をトレースすることで、ボトルネックに関する重要な分析情報を得ることができます。

APK に関する考慮事項

デバッグ バリアントは、スタック サンプルのトラブルシューティングやシンボル化に役立ちます。 パフォーマンスに深刻な影響を与えますAndroid 10(API)を搭載するデバイス レベル 29)以上では、profileable android:shell="true" を リリースビルドでのプロファイリングを有効にします。

本番環境レベルのコード圧縮構成を使用します。影響 リソースの数を制限すると、パフォーマンスに大きな影響を与える可能性があります。一部 ProGuard 構成によってトレースポイントが削除されるため、 テスト対象の構成を指定します。

コンパイル

デバイス上のアプリを既知の状態(通常は speed)にコンパイルします。 speed-profile。バックグラウンドのジャストインタイム(JIT)アクティビティにより、重大なパフォーマンス オーバーヘッドが生じる可能性があり、テスト実行の間に APK を再インストールする際に頻繁に生じます。これを行うコマンドは次のとおりです。

adb shell cmd package compile -m speed -f com.google.packagename

speed コンパイル モードでは、アプリが完全にコンパイルされます。speed-profile モードでは、アプリの使用中に収集された利用コードパスのプロファイルに沿ってアプリがコンパイルされます。プロファイルを一貫して正確に収集することは難しいため、使用する場合は、想定どおりに収集されていることを確認してください。プロファイルは次の場所にあります。

/data/misc/profiles/ref/[package-name]/primary.prof

Macrobenchmark では、コンパイル モードを直接指定できます。

システムに関する考慮事項

低レベルと高忠実度の測定では、デバイスを調整します。同じデバイス、同じ OS バージョンで、A/B 比較を実行します。同じデバイスタイプであっても、パフォーマンスが大幅に異なる場合があります。

ユーザーに root 権限のあるデバイスでは、Microbenchmark に lockClocks スクリプトを使用することを検討してください。特に、これらのスクリプトは次の処理を行います。

  • CPU を固定周波数で配置する。
  • 小さなコアを無効にして、GPU を構成する。
  • サーマル スロットリングを無効にする。

lockClocks スクリプトは、アプリの起動、DoU テスト、ジャンクテストなど、ユーザー エクスペリエンスに重点を置いたテストに使用することはおすすめしませんが、Microbenchmark テストではノイズを減らすために不可欠な場合があります。

可能であれば、Macrobenchmark などのテスト フレームワークを使用することを検討してください。 測定時のノイズを減らし、不正確な測定を防ぐことができます。

アプリの起動が遅い: 不要なトランポリン アクティビティ

トランポリン アクティビティでは、アプリの起動時間が不必要に長くなる可能性があり、アプリがそれを行っているかどうかを認識することが重要です。次のトレース例に示すように、最初のアクティビティによってフレームが描画されることなく、1 つの activityStart の直後に別の activityStart が続きます。

alt_text 図 1. トランポリン アクティビティを示すトレース。

これは、通知エントリポイントと通常のアプリ起動エントリポイントの両方で発生し、多くの場合はリファクタリングで対処できます。たとえば、別のアクティビティが実行される前にそのアクティビティを使用してセットアップを行う場合は、そのコードを再利用可能なコンポーネントまたはライブラリに分解します。

頻繁に GC をトリガーする不要な割り当て

Systrace では、ガベージ コレクション(GC)が想定よりも頻繁に発生することがあります。

次の例では、長時間実行オペレーション中の 10 秒ごとに、アプリが時間の経過とともに不必要に、一貫して割り当てを行っている可能性があることを示しています。

alt_text 図 2. GC イベント間のスペースを示すトレース。

また、Memory Profiler を使用する場合、特定のコールスタックが割り当ての大部分を行っていることがわかります。コードの保守が困難になる可能性があるため、すべての割り当てを積極的に排除する必要はありません。代わりに、割り当てのホットスポットに取り組むことから始めます。

ジャンクのあるフレーム

グラフィック パイプラインは比較的複雑で、最終的にフレーム落ちが発生するかどうかの判断には微妙な違いが生じる可能性があります。場合によっては、プラットフォームがバッファリングを使用してフレームを「レスキュー」できます。しかし、その微妙な違いの大部分を無視して、アプリの観点から問題のあるフレームを特定できます。

アプリからの処理をほとんど必要とせずにフレームが描画されているとき、60 FPS デバイスでは Choreographer.doFrame() トレースポイントは 16.7 ミリ秒周期で発生します。

alt_text 図 3. 頻繁な高速フレームを示すトレース。

ズームアウトしてトレースを移動すると、完了までにもう少し時間がかかるフレームが表示されることがありますが、割り当てられている 16.7 ミリ秒よりも時間がかかることはないため問題ありません。

alt_text 図 4. 処理が周期的にバーストする、頻繁な高速のフレームを示すトレース。

図 5 に示すように、この規則的な頻度が中断された場合は、ジャンクのあるフレームになります。

alt_text 図 5. ジャンクのあるフレームを示すトレース。

これらを特定する練習をしましょう。

alt_text 図 6.さらにジャンクのあるフレームを示すトレース。

場合によっては、どのビューがインフレートされているか、または RecyclerView が何を行っているかに関する詳細情報を得るために、トレースポイントにズームインする必要があります。また、さらなる調査が必要となることもあります。

ジャンクのあるフレームを特定して原因をデバッグする方法について詳しくは、遅いレンダリングをご覧ください。

RecyclerView に関するよくある間違い

RecyclerView のバッキング データ全体を不必要に無効にすると、フレームのレンダリング時間が長くなり、ジャンクが発生する可能性があります。代わりに、検出ルールの数を 変更されたデータのみが無効になります。

コストのかかる notifyDatasetChanged() を回避する方法については、動的データを表示するをご覧ください。 これにより、コンテンツが完全に置き換えられるのではなく、更新されることになります。

すべてのネストされた RecyclerView が適切にサポートされていない場合、内部の RecyclerView が毎回完全に再作成される可能性があります。ネストされたフィールドでは、 内部 RecyclerView には、RecycledViewPool を設定して、 ビューは、すべての内部 RecyclerView 間でリサイクルできます。

十分なデータをプリフェッチしなかったり、適切なタイミングでプリフェッチしなかったりすると、ユーザーがサーバーから追加のデータを待たなければならないときに、スクロール リストの一番下まで到達しづらくなる可能性があります。フレームのデッドラインには間に合うため、これは厳密にはジャンクではありませんが、ユーザーがデータを待つ必要がないようにプリフェッチのタイミングと量を変更すると、ユーザー エクスペリエンスが大幅に改善する可能性があります。

アプリをデバッグする

アプリのパフォーマンスをデバッグするさまざまな方法は次のとおりです。詳しくは、 次の動画では、システム トレースと Android Studio Profiler。

Systrace を使用してアプリの起動をデバッグする

アプリの起動プロセスの概要については、アプリの起動時間をご覧ください。システム トレースの概要については、次の動画をご覧ください。

起動タイプの曖昧さは、次のステージで除去できます。

  • コールド スタート: 保存済み状態のない新しいプロセスの作成から開始します。
  • ウォーム スタート: プロセスを再利用するか、保存済み状態からプロセスを再作成してアクティビティを再作成します。
  • ホット スタートアップ: アクティビティを再起動し、インフレーションから開始します。

デバイス上のシステム トレース アプリを使用して Systrace をキャプチャすることをおすすめします。Android 10 以降の場合は、Perfetto を使用します。Android 9 以前の場合は、Systrace を使用します。トレース ファイルは、ウェブベースの Perfetto トレース ビューア。詳細については、システム トレースの概要をご覧ください。

次の点を確認してください。

  • モニターの競合: モニターで保護されたリソースの競合によって、 大幅に低下します。
  • 同期バインダー トランザクション: アプリのクリティカル パスに存在する不要なトランザクションを探します。必要なトランザクションが高額な場合は、関連するプラットフォーム チームと連携して改善を検討してください。

  • 同時実行 GC: これは一般的で比較的影響が小さくなりますが、 Android Studio のメモリで調べることを検討してください。 Profiler を使用します。

  • I/O: 起動時に実行される I/O をチェックし、長いストールを探します。

  • 他のスレッドでの重要なアクティビティ: UI スレッドの妨げになる可能性があるため、起動時のバックグラウンド処理に注意します。

Google Cloud コンソールから起動が完了したら、reportFullyDrawn を呼び出すことをおすすめします。 改善されたアプリの起動指標のレポートに関するアプリの視点。reportFullyDrawn の使用方法については、全画面表示までの時間のセクションをご覧ください。RFD で定義された開始時間は Perfetto トレース プロセッサ ユーザーに表示されるトレース イベントが生成されます。

デバイスで System Tracing を使用する

システム レベルのアプリである System Tracing を使用して、デバイスのシステム トレースをキャプチャできます。このアプリを使用すると、デバイスを接続したり adb に接続したりすることなく、デバイスからトレースを記録できます。

Android Studio Memory Profiler を使用する

Android Studio Memory Profiler を使用して、メモリ プレッシャーを検査できます。 メモリリークや不適切な使用パターンが原因です。オブジェクトの割り当てをリアルタイムで確認できます。

アプリのメモリの問題を修正するには、Memory Profiler を使用して GC が発生する理由と頻度をトラッキングします。

アプリのメモリをプロファイリングする手順は次のとおりです。

  1. メモリの問題を検出する。

    重視するユーザー ジャーニーのメモリ プロファイリング セッションを記録します。 図 7 に示すように、オブジェクトの数が増加していることを確認します。 図 8 に示すように GC につながります

    alt_text 図 7. オブジェクト数を増やす

    alt_text 図 8.ガベージ コレクション。

    メモリ負荷を増大させているユーザー ジャーニーを特定したら、メモリ負荷の根本原因を分析します。

  2. メモリ負荷のホットスポットを診断する。

    図 9 のように、タイムラインで範囲を選択し、[割り当て] と [シャローサイズ] の両方を可視化します。

    alt_text 図 9.[Allocations] と [Shallow] の値 サイズ

    このデータは、複数の項目で並べ替えを行えます。以下に、各ビューを利用して問題を分析する例を示します。

    • クラスで並べ替え: キャッシュに保存(メモリプールから再利用)すべきオブジェクトを生成しているクラスを見つけ出す場合に便利です。

      たとえば、2,000 個のクラスのオブジェクトを 「Vertex」Allocations カウントが 2,000 ずつ増加します。 クラスで並べ替えるときにも 同じことを繰り返しますこれらのオブジェクトを再利用してガベージの生成を回避する場合は、メモリプールを実装します。

    • コールスタックで並べ替え: メモリが割り当てられているホットパス(ループ内や、大量の割り当て処理を行う特定の関数内など)を見つける場合に便利です。

    • Shallow Size: オブジェクト自体のメモリのみをトラッキングします。役に立つ 大部分がプリミティブ値のみで構成されるシンプルなクラスをトラッキングするために使用します。

    • Retained Size: オブジェクトと、そのオブジェクトのみが参照する参照に起因するメモリの合計サイズを示します。複雑なオブジェクトによるメモリ負荷をトラッキングする場合に使用します。この値を取得するには、メモリを使い切ってください ダンプされ、図 10 のように [Retained Size] が列として追加されています。 図 11 に示しています

      alt_text 図 10.完全なメモリダンプ。

      保持サイズ列。
      図 11. Retained Size 列
  3. 最適化の効果を測定する。

    GC はより明確で、メモリの影響を簡単に測定できる 役立ちます最適化によりメモリ負荷が軽減されると、GC が少なくなります。

    最適化の影響を測定するには、プロファイラのタイムラインで GC 間の時間を測定します。GC 間の時間が長くなっていることがわかります。

    メモリの改善による最終的な影響は次のとおりです。

    • アプリが頻繁に起動しない場合は、メモリ不足によるシャットダウンが減少する可能性が高くなります。 メモリ不足に陥ります。
    • GC を減らすことで、特に P99 でジャンク指標が改善されます。これは、GC が CPU の競合を引き起こし、その間のレンダリング処理を遅延させる可能性があるものだからです。