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

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

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

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

起動レイテンシ

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

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

  • 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 個のリンクが失敗します。たとえば リンクを HTTP から HTTPS にリダイレクトし 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 権限のあるデバイスでは、次の lockClocks スクリプトを使用することを検討してください。 Microbenchmark。特に、これらのスクリプトは次の処理を行います。

  • 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 を呼び出すことをおすすめします。 改善されたアプリの起動指標のレポートに関するアプリの視点。Time reportFullyDrawn の使用に関する詳細をご覧ください。 RFD で定義された開始時間は Perfetto トレース プロセッサ ユーザーに表示されるトレース イベントが生成されます。

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

System Tracing というシステムレベルのアプリを使用すると、システムをキャプチャできます。 実行できます。このアプリでは、デバイスを使用せずにトレースを記録できます。 接続するか adb に接続する必要はありません。

Android Studio Memory Profiler を使用する

Android Studio Memory Profiler を使用して、メモリ プレッシャーを検査できます。 メモリリークや不適切な使用パターンが原因です。Cloud Shell では、 オブジェクト割り当てのビューを表示します

アプリのメモリの問題を解決するには、 Memory Profiler は、GC が発生する理由と頻度を追跡します。

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

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

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

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

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

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

  2. メモリ プレッシャーのホットスポットを診断する。

    タイムラインで範囲を選択して、割り当てShallow Size(シャローサイズ)を使用します(図 9 を参照)。

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

    このデータは、複数の項目で並べ替えを行えます。以下に例をいくつか示します。 各ビューが問題の分析にどのように役立つかを説明します

    • Arrange by class: クラス別に並べ替える キャッシュされなければメモリプールから再利用されるオブジェクトを生成する

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

    • Arrange by callstack: ホット ループや内部など、メモリが割り当てられているパスに 多くの割り当て作業を行う特定の関数が 必要となります

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

    • Retained Size: オブジェクトと参照による合計メモリを示します。 そのオブジェクトによってのみ参照されるオブジェクトです。メモリ位置を追跡したり 押さえておきましょう。この値を取得するには、メモリを使い切ってください になり、[Retained Size] が列として追加されます。 図 11 に示しています

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

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

    GC はより明確で、メモリの影響を簡単に測定できる 役立ちます最適化によってメモリ プレッシャーが軽減されると、 GC の数が減ります。

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

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

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