CPU Profiler を使用して CPU アクティビティを検査する

アプリの CPU 使用率を最小限にすると、より高速でスムーズなユーザー エクスペリエンスを実現したり、デバイスのバッテリー寿命を延ばしたりなど、多くのメリットがあります。

CPU Profiler を使用すると、アプリの CPU 使用量やスレッド アクティビティをリアルタイムで確認したり、記録されたメソッド トレース、関数トレース、システム トレースの詳細を調べたりできます。

CPU Profiler で記録、表示する特定の情報は、選択した記録構成によって決まります。

  • システム トレース: アプリがシステム リソースをどのように使用しているかを調べることができる、詳細情報を取得します。
  • メソッドと関数のトレース: アプリプロセスの各スレッドで、一定の期間に実行されたメソッド(Java)または関数(C/C++)や、各メソッドまたは関数が実行中に消費した CPU リソースを確認できます。また、メソッド トレースを使用して、呼び出し元や呼び出し先を特定できます。呼び出し元は別のメソッドまたは関数を呼び出すメソッドまたは関数であり、呼び出し先は別のメソッドまたは関数によって呼び出されるメソッドまたは関数です。この情報を使用して、リソースを大量に消費する特定のタスクを頻繁に呼び出す原因になっているメソッドまたは関数を特定し、アプリのコードを最適化して不要な動作を回避することができます。

    メソッド トレースを記録する際は、サンプリング記録または計測記録を選択できます。関数トレースを記録するときは、サンプリング記録のみ使用できます。

各トレース オプションの使用と選択について詳しくは、記録構成を選択するをご覧ください。

CPU Profiler の概要

CPU Profiler は次の手順で開きます。

  1. [View] > [Tool Windows] > [Profiler] を選択するか、ツールバーの [Profile]()をクリックします。

    [Select Deployment Target] ダイアログが表示されたら、プロファイリング用にアプリをデプロイするデバイスを選択します。USB 経由でデバイスを接続しているにもかかわらずデバイスがリストに表示されない場合は、USB デバッグが有効になっているか確認します。

  2. CPU タイムラインの任意の場所をクリックして CPU Profiler を開きます。

CPU Profiler を開くと、アプリの CPU 使用率とスレッド アクティビティが、図 1 のように画面に表示されます。

図 1. CPU Profiler のタイムライン。

図 1 に示すように、CPU Profiler のデフォルトのビューには次の項目が含まれます。

  1. イベントのタイムライン: ライフサイクルでさまざまな状態に変化するアプリのアクティビティと、画面の回転イベントなどユーザーによるデバイス操作を示します。Android 7.1(API レベル 25)以前を実行しているデバイスでイベントのタイムラインを有効にする方法については、詳細なプロファイリングを有効にするをご覧ください。
  2. CPU のタイムライン: アプリのリアルタイム CPU 使用率を利用可能な合計 CPU 時間の割合で表すとともに、アプリが使用しているスレッドの合計数を示します。このタイムラインには、他のプロセス(システム プロセスまたは他のアプリ)の CPU 使用率も示されるため、アプリの CPU 使用率と比較することができます。マウスをタイムラインで水平方向に動かすと、CPU 使用率の履歴データを調べることができます。
  3. スレッド アクティビティのタイムライン: アプリ プロセスの各スレッドを一覧表示し、以下の色を使用して、スレッドのアクティビティをタイムラインに沿って示します。メソッド トレースを記録したら、このタイムラインでスレッドを選択し、トレースペインでそのデータを検証することができます。
    • 緑: スレッドがアクティブであるか、CPU を使う用意ができています。つまり、スレッドは実行中または実行可能な状態です。
    • 黄: スレッドはアクティブですが、タスクを完了する前に、ディスク I/O やネットワーク I/O などの I/O 操作を待機しています。
    • グレー: スレッドがスリープ状態であり、CPU 時間を消費していません。この状態は、スレッドがまだ利用できないリソースへのアクセスを要求したときに発生することがあります。スレッドは自発的にスリープ状態になるか、要求したリソースが利用可能になるまでカーネルによってスリープ状態に置かれます。

    CPU Profiler は、JDWPProfile SaverStudio:VMStatsStudio:PerfaStudio:Heartbeat(スレッド アクティビティのタイムラインに表示される正確な名前は異なる場合があります)など、Android Studio と Android プラットフォームがアプリプロセスに追加したスレッドの CPU 使用率も報告します。Android Studio ではこのデータが報告されるため、アプリのコードによってスレッド アクティビティと CPU 使用率が実際に発生したタイミングを特定できます。

トレースを記録する

トレースの記録を開始するには、CPU Profiler の上部にあるプルダウン メニューで記録構成を選択して、[Record] をクリックします。

図 2. CPU Profiler では実行中の記録のステータス、継続時間、および種類が表示されます。

アプリを操作して、終了したら [Stop] をクリックします。プロファイラは記録された時間範囲を自動的に選択し、図 3 のようにトレースペインにトレース情報を表示します。別のスレッドのトレースを調査したい場合は、スレッド アクティビティのタイムラインで選択してください。

図 3. メソッド トレースを記録した後の CPU Profiler。

  1. 選択範囲: トレースペインで検証する記録時間の一部を決定します。メソッド トレースを初めて記録すると、CPU Profiler は CPU タイムラインの記録全体を自動的に選択します。記録された時間範囲の一部だけについてトレースデータを調べるには、ハイライトされた領域の端をドラッグします。
  2. タイムスタンプ: 記録されたトレースの開始時間と終了時間(プロファイラがデバイスから CPU 使用状況情報の収集を開始したタイミングを基準にした時間)を示します。記録全体を選択するには、タイムスタンプをクリックします。
  3. トレースペイン: 選択した時間範囲とスレッドのトレースデータを表示します。このペインは、少なくとも 1 つのメソッド トレースを記録した後にのみ表示されます。このペインでは、各スタック トレースの表示方法を(トレースタブを使用して)選択したり、実行時間の測定方法を(タイム リファレンスのプルダウン メニューを使用して)を選択したりできます。
  4. トレースペインのタブ: トレースの詳細を表示する方法を選択します。各オプションについて詳しくは、トレースの検証をご覧ください。
  5. タイム リファレンスのメニュー: 次のいずれかを選択して、各呼び出しのタイミング情報を測定します。
    • Wall clock time: タイミング情報が実際の経過時間を表します。
    • Thread time: タイミング情報として、実際の経過時間からスレッドが CPU リソースを消費していない時間を引いた時間を表します。Thread time はすべてのメソッドで常に Wall clock time 以下になります。Thread time を使用すると、特定のメソッドまたは関数によるスレッドの実際の CPU 使用量について詳しく把握できます。
  6. フィルタ: 関数、メソッド、クラス、パッケージ名でトレースデータをフィルタします。たとえば、[Filter]()をクリックするか、Ctrl+F(Mac の場合は Command+F)を押して、検索フィールドに名前を入力すると、特定の呼び出しに関するトレースデータをすばやく見つけることができます。[Call chart] タブや [Flame chart] タブでは、検索クエリに合致する呼び出し、パッケージ、クラスを含むコールスタックがハイライト表示されます。[Top down] タブと [Bottom up] タブでは、このコールスタックは他のトレース結果よりも優先されます。検索フィールドの横にあるチェックボックスをオンにして、次のオプションを有効にすることもできます。
    • Regex: 検索に正規表現を含める場合は、このオプションを使用します。
    • Match case: 検索クエリで大文字と小文字を区別する場合は、このオプションを使用します。

記録構成を選択する

トレース情報の記録を始める前に、取得したいプロファイリング情報の記録構成を適切に選択してください。

  • Sample Java Methods: アプリの Java ベースのコード実行中に、アプリのコールスタックを頻繁に取得します。プロファイラは取得したデータセットを比較し、アプリのコード実行に関するタイミングとリソース使用量情報を導き出します。

    サンプリング ベースのトレースに固有の問題として、コールスタックの取得後にアプリで開始されたメソッドが次のコールスタックの取得前に終了した場合、そのメソッド呼び出しがプロファイラのログに記録されない問題があります。このようなライフサイクルの短いメソッドを追跡する場合は、インストルメント化されたトレースを使用する必要があります。

  • Trace Java Methods: アプリを実行時にインストルメント化して、各メソッド呼び出しの最初と最後にタイムスタンプを記録します。タイミング情報や CPU 使用率などのメソッド トレース データを生成するために、タイムスタンプが収集、比較されます。

    各メソッドのインストルメント化に伴うオーバーヘッドにより、実行時のパフォーマンスが低下し、プロファイリング データに影響がおよぶ場合があるので注意してください。これは、ライフサイクルが比較的短いメソッドの場合に顕著です。また、アプリが短時間で多数のメソッドを実行すると、プロファイラがファイルサイズの制限をすぐに超えてしまい、それ以上トレースデータを記録できなくなる可能性があります。

  • Sample C/C++ Functions: アプリのネイティブ スレッドのサンプル トレースを取得します。この構成を使用するには、Android 8.0(API レベル 26)以上のデバイスにアプリをデプロイする必要があります。

    内部的には、この構成は simpleperf を使用して、アプリのネイティブ コードをトレースします。特定のデバイス CPU をサンプリングする場合や、高精度のサンプリング期間を指定する場合など、simpleperf の追加オプションを指定するには、simpleperf をコマンドラインから使用します。

  • Trace System Calls: アプリがシステム リソースをどのように使用しているか検査できる詳細情報を取得します。スレッドの状態の正確なタイミングと継続時間を調べたり、すべてのコアにおいて CPU ボトルネックが発生している箇所を視覚化したり、カスタム トレース イベントを追加して分析したりできます。このような情報は、パフォーマンスの問題を解決する際に重要です。この構成を使用するには、Android 7.0(API レベル 24)以降を搭載しているデバイスにアプリをデプロイする必要があります。

    このトレース構成を使用している場合は、コードをインストルメント化することで、重要なコードルーチンをプロファイラのタイムライン内で視覚的にマーキングできます。C / C++ コードをインストルメント化するには、trace.h によって提供されるネイティブ トレース API を使用します。Java コードをインストルメント化するには、Trace クラスを使用します。詳しくは、アプリのコードをインストルメント化するをご覧ください。

    このトレース構成は systrace をベースとしています。systrace コマンドライン ユーティリティを使用すると、CPU Profiler では指定できないオプションを指定できます。systrace によって提供される追加のシステムレベル データにより、ネイティブ システム プロセスを検査して、フレームの欠落や遅延をトラブルシューティングすることができます。

    Android 9(API レベル 28)以降を搭載しているデバイスの場合、「システム トレース」というシステムアプリを使用することで、デバイスのシステム トレースを記録できます。

記録構成を作成、編集、表示する

[CPU Recording Configurations] ダイアログで記録構成を作成、編集、表示するには、CPU Profiler 上部のプルダウン メニューで [Edit configurations] を選択します。

既存の記録構成の設定を表示するには、[CPU Recording Configurations] ダイアログの左ペインで設定を選択します。

新しい記録構成を作成する手順は以下のとおりです。

  1. ダイアログの左上にある [Add]()をクリックします。一部のデフォルト設定が適用された新しい構成が作成されます。
  2. 構成に名前を付けます。
  3. [Trace Technology] を選択します。
  4. サンプリング ベースの記録構成の場合は、[Sampling interval] をマイクロ秒単位(µs)で指定します。この値は、アプリのコールスタックの各サンプル間の時間を表します。指定する間隔が短ければ短いほど、記録データのファイルサイズ制限により早く到達します。
  5. 接続されたデバイスに書き込まれる記録データの [File size limit] をメガバイト単位(MB)で指定します。記録を停止すると、Android Studio はこのデータを解析してプロファイラ ウィンドウに表示します。そのため、ファイルサイズ制限を拡大して大量のデータを記録すると、Android Studio ではファイルの解析により長い時間がかかり、応答しなくなる場合があります。

    注: Android 8.0(API レベル 26)以降を実行している接続デバイスを使用する場合、トレースデータのファイルサイズに制限はなく、この値は無視されます。ただし、各記録の後、デバイスが収集するデータの量には引き続き注意する必要があります。Android Studio での大きなトレース ファイルの解析が難しくなる場合があります。たとえば、短いサンプリング間隔でサンプリング ベースのトレースを記録しているか、またはアプリが短い間に多くのメソッドを呼び出すときにインストルメント化されたトレースを記録している場合は、大きなトレース ファイルが短時間で生成されます。

  6. 変更を受け入れて他の設定を引き続き変更するには、[Apply] をクリックします。適用した変更をすべて受け入れてダイアログを閉じるには、[OK] クリックします。

Debug API を使用した CPU アクティビティの記録

Debug API を使用すると、CPU Profiler 内で CPU アクティビティの記録を開始、停止する機能をアプリに付与することができます。

CPU Profiler は、アプリが startMethodTracing(String tracePath) を呼び出すと記録を開始し、アプリが stopMethodTracing() を呼び出すと記録を停止します。この API によってトリガーされた CPU アクティビティを記録している間、CPU Profiler には、アクティブな CPU 記録構成として Debug API が表示されます。

Debug API を使用して CPU アクティビティの記録を制御するには、インストルメント化したアプリを Android 8.0(API レベル 26)以降を搭載しているデバイスにデプロイします。

詳細については、アプリをインストルメント化してトレースログを生成するをご覧ください。

アプリ起動時の CPU アクティビティの記録

アプリの起動中に CPU アクティビティの記録を自動的に開始するには、次の手順で設定します。

  1. [Run] > [Edit Configurations] の順に選択します。
  2. [Profiling] タブで、[Start recording a method trace on startup] の横にあるチェックボックスをオンにします。
  3. メニューから CPU 記録構成を選択します。
  4. [Apply] をクリックします。
  5. [Run] > [Profile] を選択し、Android 8.0(API レベル 26)以上が搭載されているデバイスにアプリをデプロイします。

トレースをエクスポートする

CPU プロファイラで CPU アクティビティを記録した後、そのデータを .trace ファイルとしてエクスポートして、他のデベロッパーと共有したり後で検証したりできます。

CPU タイムラインからトレース ファイルをエクスポートする手順は以下のとおりです。

  1. CPU タイムラインで、エクスポートする記録済みトレースを右クリックします。
  2. プルダウン メニューで [Export trace] を選択します。
  3. ファイルを保存する場所を参照し、ファイル名を指定して [OK] をクリックします。

[Sessions] ペインからトレース ファイルをエクスポートする手順は以下のとおりです。

  1. [Sessions] ペインで、エクスポートする記録済みトレースを右クリックします。
  2. セッション エントリの右側にある [Export method trace] または [Export system trace] をクリックします。
  3. ファイルを保存する場所を参照し、ファイル名を指定して [OK] をクリックします。

トレースをインポートする

Debug API または CPU Profiler を使用して作成した .trace ファイルをインポートすることができます。

トレース ファイルをインポートするには、プロファイラの [Sessions] パネルで [Start new profiler session]()をクリックして、[Load from file] を選択します。

CPU Profiler にインポートしたトレースは、直接 CPU Profiler 内でキャプチャしたトレースと同じように検査することができます。ただし、以下の点が異なります。

  • CPU ライムラインに CPU アクティビティは表示されません。
  • スレッド アクティビティのタイムラインに表示されるのは、実際のスレッド状態(実行中、待機中、スリープ中など)ではなく、各スレッドのトレースデータが利用可能な場所のみです。

トレースを検証する

CPU Profiler のトレースペインにあるタブでは、記録したトレースの情報を確認する方法を選択できます。

メソッド トレースと関数のトレースの場合は、[Call Chart]、[Flame Chart]、[Top Down]、[Bottom Up] タブから選択できます。システム トレースの場合は、[Trace Events]、[Flame Chart]、[Top Down]、[Bottom Up] タブから選択できます。

Call Chart タブを使用してトレースを検証する

[Call Chart] タブにはメソッド トレースまたは関数トレースがグラフィックで表示されます。呼び出しの期間とタイミングが横軸で、呼び出し先が縦軸で示されます。システム API へのメソッド呼び出しはオレンジで、アプリのメソッドへの呼び出しは緑で、サードパーティの API(Java 言語 API を含む)への呼び出しは青で示されます。図 4 は呼び出しチャートの例です。セルフ時間、子時間、特定のメソッドまたは関数の合計時間の概念を示しています。これらの概念については、Top Down タブと Bottom Up タブを使用してトレースを検証する方法で説明します。

図 4. セルフ時間、子時間、メソッド D の合計時間を示した呼び出しチャートの例。

ヒント: メソッドまたは関数のソースコードにジャンプするには、メソッドまたは関数を右クリックして [Jump to Source] を選択します。この操作は、すべてのトレースペイン タブで行えます。

Flame Chart タブを使用してトレースを検証する

[Flame Chart] タブには、同一のコールスタックが集約される反転された呼び出しチャートが表示されます。つまり、同じシーケンスの呼び出し元を共有する同一のメソッドまたは関数が集められ、(呼び出しチャートのようにメソッドまたは関数を複数の短いバーで表示せずに)フレーム チャートに 1 つの長いバーとして表されます。これにより、最も時間を消費しているメソッドまたは関数がわかりやすくなります。ただし、この場合横軸はタイムラインを表さず、その代わりに各メソッドの実行にかかった相対的な時間が示されます。

この概念について、図 5 に示す呼び出しチャートを例に説明します。メソッド D が B に対して複数の呼び出し(B1、B2、B3)を実行し、B へのこれらの呼び出しの一部が C に対して呼び出し(C1 と C3)を実行していることに注意してください。

図 5. 共通の順序の呼び出し元を共有している複数のメソッド呼び出しが含まれた呼び出しチャート。

B1、B2、B3 は、同じ順序の呼び出し元(A → D → B)を共有しているため、図 6 のように集約されます。同様に、同じシーケンスの呼び出し元(A → D → B → C)を共有している C1 と C3 も集約されます。C2 の呼び出し元の順序は異なるため(A → D → C)、集約に含まれないことに注意してください。

図 6. 同じコールスタックを共有している同一のメソッドの集約。

集計された呼び出しは、図 7 に示すようにフレーム チャートの作成に使用されます。フレーム チャート内の特定の呼び出しについて、最も CPU 時間を消費する呼び出し先が最初に表示されることに注意してください。

図 7. 図 5 の呼び出しチャートをフレーム チャートで表した図。

Top Down タブと Bottom Up タブを使用してトレースを検証する

[Top Down] タブには呼び出しの一覧が表示され、メソッドノードまたは関数ノードを展開すると呼び出し先が表示されます。図 8 は、図 4 の呼び出しチャートのトップダウン グラフを示しています。グラフ内の各矢印は、呼び出し元から呼び出し先を指しています。

図 8 に示すように、[Top Down] タブでメソッド A のノードを展開すると、その呼び出し先であるメソッド B とメソッド D が表示されます。さらに、メソッド D のノードを展開すると、その呼び出し先であるメソッド B とメソッド C が表示され、メソッド B でも同じように呼び出し先が表示されます。[Flame Chart] タブの場合と同じように、トップダウン ツリーでは同じコールスタックを共有している同一のメソッドのトレース情報が集約されます。つまり、[Flame Chart] タブの表示は [Top Down] タブをグラフィックで表示したものです。

[Top Down] タブには、各呼び出しに費やされた CPU 時間の説明するために以下の情報が表示されます(時間は、選択した範囲にあるスレッドの合計時間の割合としても表されます)。

  • Self: 図 4 のメソッド D が示すように、メソッドまたは関数の呼び出しが自身のコードの実行に費やした時間であり、呼び出し先の実行に費やした時間ではありません。
  • Children: 図 4 のメソッド D が示すように、メソッドまたは関数呼び出しがその呼び出し先の実行に費やした時間であり、自身のコードの実行に費やした時間ではありません。
  • Total: メソッドの Self 時間と Children 時間の合計です。図 4 のメソッド D が示すように、アプリがメソッド呼び出しの実行に費やした合計時間を表します。

図 8. トップダウン ツリー。

図 9. 図 8 のメソッド C のボトムアップ ツリー

[Bottom Up] タブには、呼び出しの一覧が表示され、メソッドまたは関数のノードを展開するとその呼び出し元が表示されます。図 9 では、図 8 で示すトレース例を使用してメソッド C のボトムアップ ツリーを示しています。ボトムアップ ツリーでメソッド C のノードを開くと、その一意の各呼び出し元であるメソッド B とメソッド D が表示されます。メソッド B はメソッド C を 2 回呼び出しますが、ボトムアップ ツリーでメソッド C のノードを展開すると、メソッド B が 1 つだけ表示されることに注意してください。さらに、メソッド B のノードを展開すると、その呼び出し元であるメソッド A とメソッド D が表示されます。

[Bottom Up] タブは、メソッドまたは関数を最も多くの(または最も少ない)CPU 時間を消費したスレッド順に並べ替えるのに便利です。各ノードを調べると、これらのメソッドまたは関数を呼び出すときに最も多くの CPU 時間を消費した呼び出し元を特定することができます。トップダウン ツリーとは異なり、ボトムアップ ツリーの各メソッドまたは関数のタイミング情報は、各ツリーの最上部のメソッド(トップノード)に関連しています。CPU 時間は、記録中のスレッドの合計時間の割合としても表されます。次の表は、トップノードとその呼び出し元のメソッド(サブノード)のタイミング情報を解釈する方法を理解するのに役立ちます。

Self Children Total
ボトムアップ ツリーの最上位のメソッドまたは関数(トップノード) メソッドまたは関数がその呼び出し先ではなく自身のコードの実行に費やした合計時間。トップダウン ツリーとは異なり、このタイミング情報は、記録中におけるメソッドまたは関数の呼び出し先へのすべての呼び出しの合計時間を表しています。 メソッドまたは関数が自身のコードではなくその呼び出し先実行に費やした合計時間。トップダウン ツリーとは異なり、このタイミング情報は、記録中におけるメソッドまたは関数の呼び出し先へのすべての呼び出しの合計時間を表しています。 セルフ時間と子時間の合計。
呼び出し元(サブノード) 呼び出し元から呼び出されたときの呼び出し先のセルフ時間の合計。図 9 のボトム アップ ツリーを例として説明すると、メソッド B のセルフ時間は、メソッド B から呼び出されるたびに実行されるメソッド C のセルフ時間の合計に等しくなります。 呼び出し元から呼び出されたときの呼び出し先の子時間の合計。図 9 のボトム アップ ツリーを例として説明すると、メソッド B の子時間は、メソッド B から呼び出されるたびに実行されるメソッド C の子時間の合計に等しくなります。 セルフ時間と子時間の合計。

注: Android Studio は、特定の記録中にプロファイラがファイルサイズの制限に達すると、新しいデータの収集を停止します(ただし、記録は停止しません)。通常、インストルメント化されたトレースを実行すると、データの収集がより早く停止します。このタイプのトレースでは、サンプリング ベースのトレースよりも短い時間でより多くのデータを収集するからです。インスペクション タイムフレームを制限への到達後に発生した記録期間に拡張した場合、トレースペインのタイミング データは変化しません(新しいデータが利用できないため)。また、データが利用できない記録の部分のみを選択すると、トレースペインにはタイミング情報として NaN が表示されます。

Trace Events タブを使用してシステム トレースを検証する

[Trace Events] タブを使用して、各スレッドで発生するイベントの詳細を表示できます。

スレッドの詳細を表示するには、[Threads] ペインでスレッドを選択します。これにより、[Kernel] ペインの各 CPU コアのスレッドのアクティビティがハイライト表示され、[Trace Events] タブにスレッドのイベントが表示されます。[Trace Events] タブのイベントにマウスポインタを置くと、イベントの名前と各状況で費やした時間が表示されます。

たとえば、図 10 の場合、[Threads] パネルで RenderThread を選択しています。そのアクティビティが [Kernel] パネル内の [CPU 0] と [CPU 1] でハイライト表示されており、[Trace Events] タブには個々のイベントの経過時間が表示されます。

図 10. レンダリング スレッドの CPU アクティビティとトレース イベント。

システム トレース情報の検査については、systrace ドキュメントの Investigate UI のパフォーマンスに関する問題をご覧ください。

フレーム レンダリング データを検証する

メインスレッドでフレームがレンダリングされるまでの時間と、RenderThread UI ジャンクと低フレームレートの原因となるボトルネックを調べることができます。

フレーム レンダリング データを確認するには、[Trace System Calls] の使用を許可する構成を使用してトレースを記録します。トレースを記録したら、図 11 に示すように [FRAMES] で各フレームについての情報を探します。

図 11. 16 ミリ秒以上のフレームは赤色で表示されます。