トレースの一括分析

ProfilingManager を使用して複数のトレースを収集すると、それらを個別に調べてパフォーマンスの問題を見つけることは現実的ではなくなります。一括トレース分析を使用すると、トレースのデータセットを同時にクエリして、次のことができます。

  • 一般的なパフォーマンスの低下を特定する。
  • 統計的分布(P50、P90、P99 レイテンシなど)を計算します。
  • 複数のトレースにわたるパターンを見つける。
  • 外れ値トレースを見つけて、パフォーマンスの問題を把握してデバッグします。

このセクションでは、Perfetto Python Batch Trace Processor を使用して、ローカルに保存された一連のトレース全体で起動指標を分析し、外れ値トレースを特定して詳細な分析を行う方法について説明します。

クエリを設計する

一括分析を実行する最初のステップは、PerfettoSQL クエリを作成することです。

このセクションでは、アプリの起動レイテンシを測定するクエリの例を示します。具体的には、activityStart から最初のフレームが生成される(Choreographer#doFrame スライスの最初の発生)までの時間を測定することで、アプリの制御下にあるアプリ起動レイテンシを測定できます。図 1 は、クエリするセクションを示しています。

トレースのタイムライン ビュー。activityStart イベントから最初の Choreographer#doFrame イベントまでの期間がハイライト表示されています。
図 1. `activityStart` から生成された最初のフレームまでのトレース セクション。
CREATE OR REPLACE PERFETTO FUNCTION find_slices(pattern STRING) RETURNS
TABLE (name STRING, ts LONG, dur LONG) AS
SELECT name,ts,dur FROM slice WHERE name GLOB $pattern;

CREATE OR REPLACE PERFETTO FUNCTION generate_start_to_end_slices(startSlicePattern STRING, endSlicePattern STRING, inclusive BOOL) RETURNS
TABLE(name STRING, ts LONG, dur LONG) AS
SELECT name, ts, MIN(startToEndDur) as dur
FROM
  (SELECT S.name as name, S.ts as ts, E.ts + IIF($inclusive, E.dur, 0) - S.ts as startToEndDur
  FROM find_slices($startSlicePattern) as S CROSS JOIN find_slices($endSlicePattern) as E
  WHERE startToEndDur > 0)
GROUP BY name, ts;

SELECT ts,name,dur from generate_start_to_end_slices('activityStart','*Choreographer#doFrame [0-9]*', true)

Perfetto UI 内でクエリを実行し、クエリ結果を使用してデバッグ トラックを生成し(図 2)、タイムライン内で可視化できます(図 3)。

起動クエリのデバッグ トラックを作成する方法を示す Perfetto UI のスクリーンショット。
図 2. 起動クエリのデバッグ トラックを作成します。
Perfetto UI のタイムライン ビュー。起動クエリ用に生成されたデバッグ トラックが表示されている。
図 3. 起動クエリのデバッグ トラックを生成しました。

Python 環境を設定する

ローカルマシンに Python をインストールし、必要なライブラリをインストールします。

pip install perfetto pandas plotly

一括トレース分析スクリプトを作成する

次のサンプル スクリプトは、Perfetto の Python BatchTraceProcessor を使用して、複数のトレースでクエリを実行します。

from perfetto.batch_trace_processor import BatchTraceProcessor
import glob
import plotly.express as px

# Collect all trace files in the local directory
traces = glob.glob('*.perfetto-trace')

if not traces:
    print("No .perfetto-trace files found in the current directory.")
    exit(1)

if __name__ == '__main__':
    # Process all traces in parallel to aggregate metrics across runs
    with BatchTraceProcessor(traces) as btp:
        query = """
        CREATE OR REPLACE PERFETTO FUNCTION find_slices(pattern STRING) RETURNS
        TABLE (name STRING, ts LONG, dur LONG) AS
        SELECT name,ts,dur FROM slice WHERE name GLOB $pattern;

        CREATE OR REPLACE PERFETTO FUNCTION generate_start_to_end_slices(startSlicePattern STRING, endSlicePattern STRING, inclusive BOOL) RETURNS
        TABLE(name STRING, ts LONG, dur LONG) AS
        SELECT name, ts, MIN(startToEndDur) as dur
        FROM
          (SELECT S.name as name, S.ts as ts, E.ts + IIF($inclusive, E.dur, 0) - S.ts as startToEndDur
          FROM find_slices($startSlicePattern) as S CROSS JOIN find_slices($endSlicePattern) as E
          WHERE startToEndDur > 0)
        GROUP BY name, ts;

        SELECT ts,name,dur / 1000000 as dur_ms from generate_start_to_end_slices('activityStart','*Choreographer#doFrame [0-9]*', true)
        """
        df = btp.query_and_flatten(query)
        # Plot the distribution of startup times, tracking trace file paths on
        # hover
        violin = px.violin(df, x='dur_ms', hover_data='_path', title='startup time', points='all')
        violin.show()

スクリプトについて

Python スクリプトを実行すると、次の処理が行われます。

  1. スクリプトは、ローカル ディレクトリで .perfetto-trace という接尾辞が付いたすべての Perfetto トレースを検索し、分析用のソーストレースとして使用します。
  2. このクエリは、activityStart トレース スライスからアプリによって生成された最初のフレームまでの時間に対応する起動時間のサブセットを計算する一括トレースクエリを実行します。
  3. バイオリン プロットを使用してレイテンシ(ミリ秒単位)をプロットし、起動時間の分布を可視化します。

結果を解釈する

クエリされた起動レイテンシの分布を示すバイオリン プロット。
図 4. クエリされた起動レイテンシのバイオリン プロット。

スクリプトを実行すると、スクリプトによってプロットが生成されます。この場合、プロットは 2 つの異なるピークを持つ二峰性分布を示しています(図 4)。

次に、2 つの母集団の差を求めます。これにより、個々のトレースをより詳細に調査できます。この例では、データポイント(レイテンシ)にカーソルを合わせると、トレース ファイル名を識別できるようにプロットが設定されています。次に、高レイテンシ グループの一部であるトレースのいずれかを開きます。

高レイテンシ グループからトレースを開くと(図 5)、起動中に MyFlaggedFeature という名前の追加のスライスが実行されていることがわかります(図 6)。逆に、低レイテンシの母集団(左端のピーク)からトレースを選択すると、同じスライスが存在しないことが確認されます(図 7)。この比較により、一部のユーザーに対して有効になっている特定のフィーチャー トグルが回帰をトリガーしていることがわかります。

レイテンシの高いトレースがハイライト表示されたプロット。
図 5. バイオリン プロット内の高レイテンシのデータポイント。
MyFlaggedFeature スライスによる高レイテンシの起動をハイライト表示したトレース。
図 6. 追加の「MyFlaggedFeature」スライスを含む高レイテンシのトレース起動。
MyFlaggedFeature スライスなしで低レイテンシの起動をハイライト表示するトレース。
図 7. 低レイテンシのトレース起動。

この例では、一括トレース分析を使用する多くの方法の 1 つを示します。その他のユースケースとしては、フィールドから統計情報を抽出して影響を測定する、回帰を検出するなどがあります。