ツールとパフォーマンス

Trace me if you can: 自動化されたインストルメンテーションとフィールド プロファイリング

スピーカー

  • Rahul Ravikumar

    スタッフ ソフトウェア エンジニア

概要

講演について

Android アプリを最適化するには、同時実行ワークフローとコルーチンのサスペンドを詳細に可視化する必要があります。

ローカル プロファイリングは有用なスナップショットを提供しますが、オーバーヘッドなしでこれらの分析情報を本番環境にスケーリングすることは依然として課題です。

このセッションでは、継続的なパフォーマンス フィードバック ループのアーキテクチャ ブループリントについて説明します。

androidx.tracing 2.0: バックエンドを動的にスワップし、スレッド間でコルーチン コンテキスト(Perfetto フロー)を伝播します。

スマート インストルメンテーション: ローカル ART トレースを使用してホットスポットを特定し、オーバーヘッドの少ないトレースポイントの挿入を自動化します。

マクロベンチマーク: CI の実機で回帰を追跡し、パフォーマンスの向上を検証します。

フィールド トレース: ProfilingManager を使用して、ローカル トレースと CI トレースの枠を超えます。

概要

  • Tracing 2.0 は、以前の Android トレース API に代わるもので、ラベルの長さ制限をなくし、7 倍高速に動作します。また、OpenTelemetry などのバックエンド用のカスタムシンクを構築することもできます。
  • コンテキストの伝播は、スレッド、コルーチン、プロセス間の因果関係をマッピングします。traceCoroutine を使用して非同期実行を追跡するか、コールバック ベースのコードに伝播トークンを渡すことができます。
  • Jetpack Compose 1.13 には、再コンポーズ トレースが追加されています。これは、カスタム メタデータを使用して、再コンポーズが発生する理由を示すものです。トレースには、変更された状態値と、それらをトリガーした正確なコールスタックが記録されます。
  • Android ビルドツールは、Tracing 2.0 を使用してボトルネックを特定し、ビルドを高速化します。トレースに基づく最適化により、R8 のコード圧縮が 25% 高速化し、Metalava の送信後の時間が 15% 短縮されました。

Gemini Notebook

トピック

  • トレース
  • android-performance
  • perfetto
  • context-propagation
  • jetpack-compose
  • コルーチン
  • プロファイリング

文字起こし

Android トレースの概要と進化

  • Rahul Ravikumar

    皆さん、こんにちは。

  • 担当させていただきます、ラフルと申します。

  • 私は Android デベロッパー エクスペリエンス チームのエンジニアです。

  • 私はアプリとライブラリのパフォーマンスに重点を置いており、この講演ではトレースについてお話しします。

  • トレースの詳細に入る前に、トレースについて理解を深めるために、いくつかの用語を共有しておきたいと思います。

  • この講演でトレースに言及するときは、明示的なトレース、つまりアプリの動作を把握するためにコードを手動で計測することを指しています。

  • 自動プロファイラのことではありません。

  • 手動トレースは、コードベースにブレッドクラムを残す技術です。これにより、パフォーマンスの問題やその他の問題が発生したときに、後で原因を特定できます。

  • 通常、ログよりもはるかに優れています。

  • 大規模なアプリの Logcat 出力を見たことがあるかどうかはわかりませんが、

  • 通常、これは簡単に推論できない巨大なテキストの壁です。

  • 一方、トレースははるかに優れています。

  • 可視化しやすくなります。

  • 期間があります。

  • ある程度の構造がある。

  • ツールがいくつかあるので、スライスやダイスを行って、いつ何が起こったのかを正確に把握することもできます。

  • そのため、はるかに便利です。

  • エージェントにトレースを指示して、質問することができます。

  • Trace Processor やベンチマークなどのツールを使用したり、トレースを指標に変換したりすることもできます。

  • これらはすべて、トレースでは可能ですが、Logcat では難しいことです。

  • 一方、プロファイリング(自動プロファイリング)は、通常、既知の問題に対応するために行われます。

  • たとえば、アプリの起動が遅い、またはジャンクが発生していることに気づき、原因を絞り込んで、何が起きているのかを把握したいとします。

  • この時点で、手動トレースがまだない場合は、自動プロファイリングが役立ちます。

  • たとえば、ART メソッド トレースを使用して、すべてのメソッドのエントリと終了を計測できます。

  • または、Simpleperf などのツールを使用してスタック サンプリングを調べているかもしれません。

  • そのため、すべてのメソッドのエントリと終了を使用してオーバーヘッドを高くするのではなく、コールスタックのサンプルを確認して、アプリのホットパスにあるものを確認します。

  • 2026 年現在、RAM はどこでも問題になっているため、メモリ、メモリリーク、ヒープ プロファイルに注目しているかもしれません。

  • これは、利用できるもう 1 つのプロファイラです。

  • ヒープ保持パスを確認できます。

  • どのコードがメモリを割り当てているかを確認できます。これらはすべて、このツールで行える分析の種類です。

  •  

  • これで、その語彙ができました。

  • では、新しいライブラリを構築した理由について説明します。

  • トレース API の 1.0 API は、Android Jelly Bean で初めて導入されました。

  • 2012 年のことです。

  • 14 年以上前のことです。

  • それ以降、API はそれほど進化していません。

  • いくつかの拡張機能を追加しました。

  • 非同期イベントとカウンタは Android 10 で追加されました。

  • 2019 年のことです。

  • それだけです

  • それ以降、API は進化していません。

  • しかし、エコシステムは大きく進化しています。

  • このように、プラットフォーム トレースは飛躍的に向上していますが、API の追加という点では、その恩恵はまだ開発者に届いていません。

  • これは、私たちが変えたいと考えていることです。

  • もう 1 つの大きな問題は、1.0 ではトレースのバックエンドが固定されていることです。つまり、コードにトレース セクションを追加すると、常にシステム トレース バッファに格納されます。

  • Atrace は、そのデータを所有する Android トレース サブシステムです。

  • トレースを取得してフィールド テレメトリーに変換するのは非常に困難です。まず、atrace がそのデータをどのように保存するかを理解する必要があります。

  • これは、十分に文書化されていますが、使用するのが難しい形式です。

  • これは修正したい点です。

  • そのため、トレースデータをどこにでも持ち運べるツールを提供したいと考えています。

  • また、Tracing 1.0 では、ラベルが 128 文字に制限されるという制限もありました。

  • 2012 年には妥当な制限だったかもしれませんが、2026 年にはかなり奇妙に思えます。

  • さらに悪いことに、トレースに追加のメタデータを追加することもできません。これはさらに問題です。

  • そして、オーバーヘッドがあります。

  • そのため、アプリをトレースするときは、オーバーヘッドを十分に認識する必要があります。システムを観察する行為によって、システムが内部で行っていることが変化してはならないからです。

  • そのため、再現したい競合状態がある場合、トレースによって競合状態が解消されることはありません。

  • そのため、それ以外のセマンティクスをすべて保持する必要があります。

  • Tracing 1.0 はオーバーヘッドが比較的高かったため、改善したいと考えていました。

  • 違いますね。

  • 問題についてたくさん話しました。

  • 次に、いくつかの問題を視覚的に見て、実際にどのように複合化するかを見てみましょう。

  • これは Perfetto トレースです。

  • com.example.app というアプリです。

  • メインスレッドがあり、initializeComponent という単一のスライスがあります。

  • 理想的には、もう少し詳しい情報が必要です。

  • そこで、詳細を追加しましょう。

  • これで、initializeComponent が performInitialize を呼び出すことがわかりました。

  • 初期化されているコンポーネントはまだわかりません。

  • 現在、Tracing 1 を備えたツールベルトの唯一のツールは名前であるため、スライスの名前に追加します。

  • トレース スライス名は 128 文字に制限されているため、トレースでは切り捨てられます。

  • さらに、切り捨てのオーバーヘッドも、トレース時に発生するオーバーヘッドになります。

  • この問題はすぐに発生します。

  • これは、私たちが本当に修正したかったことです。

  • Perfetto トレースと、その視覚的な表示を確認しました。

  • 次に、これらのトレース セクションを出力するコードを記述する観点から見てみましょう。

  • ここでも initializeComponent API を使用します。

  • コンテキストと名前を受け取り、performInitialize を呼び出します。

  • 次のステップ。

  • 次に、Tracing 1.0 API を使用してトレースを追加します。これを行うには、androidx.tracing.trace をインポートしてトレース ブロックを使用します。

  • 名前を付けます。

  • ここでは performInitialize です。

  • しかし、先ほどと同様に、コンポーネントの名前を知りたいので、Kotlin の文字列補間を使用して名前を追加します。

  • ここで最初の問題が発生します。

  • ここでは、128 文字数制限があります。

トレース 2.0 とパフォーマンスの概要

  • Rahul Ravikumar

    これは、Tracing 2 で修正できます。

  • androidx.tracing.trace の代わりに Tracer を使用します。

  • API は非常によく似ています。

  • trace ではなく Tracer.global.trace があります。

  • カテゴリがある。

  • カテゴリについては後ほど詳しく説明しますが、基本的には名前があります。

  • そして、トレースしようとしているコードのブロックがまだあります。

  • また、128 文字の制限もなくなりました。

  • トレースのオーバーヘッドについて説明しました。

  • もう少し詳しく見て、どのように改善されたかを確認しましょう。

  • Android 16 を搭載した Google Pixel 6a では、1 つの開始セクションと終了セクションを挿入するのに約 1,200 ナノ秒かかりました。

  • これは大したことではないように思えるかもしれませんが、多くのトレース セクションを追加する非常にホットなコードがある場合、アプリがフレームのレンダリングに費やす時間が失われる可能性があります。

  • そのため、Google はそのような時間を奪いたくないと考えています。

  • AndroidX Perfetto SDK では、この点が大幅に改善されています。

  • これは、Composition Tracing 用に導入した試験運用版の SDK で、実際に状況を大幅に改善しました。

  • この SDK は JNI バインディングを含む Perfetto C SDK であり、C SDK は APK 内で約 4 MB を占めるため、この SDK をリリースすることはおすすめしません。

  • このような事態はあってはならないことです。

  • Tracing 2 では、専用のライブラリが用意されています。

  • オーバーヘッドが大幅に改善され、Tracing 2 は 7 倍高速になりました。これにより、インフラストラクチャにかかる時間が短縮され、やりたいことに使える時間が増えます。

  • このような機能によって、以前はできなかったどのようなことが可能になるでしょうか。

  • たとえば、Metro のランタイム トレース機能があります。

  • Metro は、依存関係インジェクション ライブラリです。

  • Kotlin マルチプラットフォームをサポートしています。

  • Kotlin コンパイラ プラグインを使用します。

  • 依存関係の注入に必要なすべてのコードを生成し、Tracing 2.0 と統合して、コード内のすべての @Inject のコストを簡単に可視化できるようにします。

  • 例を挙げましょう。

  • アプリグラフがあるとします。

  • Metro では、すべてがグラフにスコープされるため、AnalyticsTracker という 1 つのフィールドがあります。

  • provides バインディングもあります。

  • OkHttpClient を実装として持つネットワーク クライアントがあり、AnalyticsTracker の実装は AnalyticsTrackerImpl から取得されます。ネットワーク クライアントのインスタンスが必要ですが、グラフはその提供方法を認識しています。

  • つまり、コンパイラ プラグインは、内部で次のようなコードを生成します。

  • したがって、AnalyticsTracker がある場合、ゲッターはネットワーク クライアントのインスタンスを作成し、それを AnalyticsTrackerImpl のコンストラクタに渡します。これは適切な方法です。

  • しかし、トレースを有効にすると、TrackerImpl だけでなく、必要なすべての依存関係の作成コストを確認できます。

  • これにより、タイプのインスタンス化の費用だけでなく、トレース内のすべての依存関係も確認できるようになりました。これは非常に強力です。

  • この情報を使用して、金融アプリの Wise はアプリの起動時間を 45% 短縮しました。

  • アプリの起動時に初期化する必要のないものを遅らせることができました。また、スコープが間違っている項目も特定できました。シングルトンであるべきものが、実際にはシングルトンではなかったというものです。これらは、コード内で確認して簡単に修正できます。

  • スタートアップの指標を見ると、非常に印象的です。

  • 初期フレームまでの時間が短縮されただけでなく、完全表示までの時間も短縮されました。

  • 別のパートナーである BandLab も、CI で同じ情報を使用しています。

  • そのため、個々の @Inject バインディングのコストだけでなく、そのコストを発生させたスレッドも把握できます。

  • それがわかれば、メインスレッド上の高負荷な @Inject バインディングをすべて見つけることができます。CI でクエリを実行して、高負荷なバインディングをすべてリストアップし、それらを導入した変更に帰属させることができます。これで、実際の変更を特定して修正を試みることができます。

コンテキストの伝播、コルーチン、Jetpack Compose

  • Rahul Ravikumar

    違いますね。

  • ここまでは、Tracing 1 と比較して改善された点について説明してきましたが、ここからは、Tracing 2 で新たに可能になった機能について説明します。

  • コア機能の一つにコンテキスト伝播があります。最新のアプリは複雑化が進んでおり、ワークロードは複数のスレッド、コルーチン、プロセスにまたがって実行されます。

  • リクエストが各スレッドとプロセスの間でどのように伝播するかを可視化できるようにしたいと考えています。

  • これを可能にするのがコンテキストの伝播です。

  • そのため、スライスのリストだけを提供し、何が原因で何が起こったのかを説明しない場合、それらは有用ですが、推論するのはそれほど簡単ではありません。

  • しかし、代わりに、これが起こったイベントの因果関係の連鎖であると伝えた場合、何が起こっているかを把握し、各スライスのコストを関連付けることがはるかに簡単になります。

  • コンテキストの伝播によって、これが可能になります。

  • コンテキストの伝播は完全にサポートされています。実際の例を次に示します。

  • com.example.app の Perfetto トレースを振り返ってみましょう。performInitialize に時間がかかりすぎていて、コストを遅らせるか、別のスレッドに移動したいとします。

  • これは完全に実現可能です。

  • そこで、performInitialize を別のスレッドに移動したとします。

  • これで、スライスの階層的な性質が失われました。

  • そのため、コードを確認しない限り、performInitialize の原因が initializeComponent であることがわからなくなります。

  • 理想的な世界では、A が B を引き起こしたことを示す矢印があり、イベントの因果関係を推論できます。

  • プラットフォーム上のバインダー トランザクションで実際にこれを行うシステム トレースを見たことがあるかもしれません。

  • この図では、Compose ベンチマークが system_server に対してバインダー トランザクションを行い、フレームを準備していることがわかります。

  • これは、プラットフォーム トレースの観点からは常に可能だったことですが、アプリ デベロッパーが利用できるツールはこれまでありませんでした。

  • 現在、Kotlin コルーチンのコンテキスト伝播は Tracing 2.0 で完全にサポートされています。

  • 何もする必要はありません。

  • デフォルトで利用できます。

  • ここでは、fetchProfile という 1 つの suspend メソッドがあります。

  • 別の停止メソッドである client.get を呼び出し、Dispatchers.Default に切り替えてレスポンスを逆シリアル化します。

  • これは、非常に定型的なコルーチン コードであり、おそらく毎日扱っているものです。

  • ここで、これをトレースするとします。

  • トレース セクションを追加します。

  • ここでは、トレースの代わりに、traceCoroutine API を使用します。これは、コードのサスペンド ブロックであるためです。次に、Dispatchers.Default にコンテキストを切り替える前の内部呼び出しに、同じ traceCoroutine API を使用して別の内部トレース セクションを追加します。

  • トレースでは、次のように表示されます。

  • この例では、処理が worker-1 で開始された後、コンテキストが切り替わって worker-3 に移動したことがわかります。トレースには、これを示す矢印が表示されます。

  • それだけでなく、中央の四角形を拡大すると、deserialize が実際に worker-1 で開始され、withContext の呼び出しによってコンテキスト スイッチが発生し、worker-3 に引き継がれ、fetchProfile が worker-3 で再開されたことがわかります。これは、コルーチン メカニズムがコンテキスト スイッチの発生回数を最小限に抑えようとするためです。

  • コルーチンが中断された正確なタイミング、再開されたタイミング、イベントの完全な因果関係を確認できます。

  • Tracing 2 で可能になるもう 1 つの機能は、トレースにカスタム メタデータを追加できることです。

  • 以前は、トレース セクションの名前しか使用できませんでしたが、現在は、メタデータ ブロックを追加して、必要な数のメタデータ エントリを追加できるようになりました。

  • これをトレースに追加できます。

  • 実際のトレースを見ると、追加したすべてのメタデータが実際にトレースに含まれており、すべてのツールで使用できるようになっていることがわかります。

  • これにより、これまで埋め込みたいと思っていたすべての追加情報を埋め込むことができます。

  • トレースによって可能になる抽象的な機能について説明してきましたが、これらすべてが真価を発揮するのが、Jetpack Compose の新機能である再コンポジション トレースです。

  • これは Compose 1.13 の一部としてリリースされているため、今すぐご利用いただけます。

  • 例を挙げましょう。

  • これは、皆さんがよくご存じの Composition Tracing です。

  • これは Compose の初期から存在しています。

  • コンポーザブルとそのすべてのサブスライスの相対的なコストと、それらが発生したスレッドでのそれぞれのコストが表示されます。

  • これはとても簡単です。

  • コンテキストはわかりません。

  • この指標では、その理由を把握することはできません。

  • 再コンポーズされた理由は表示されません。

  • ズームアウトすると、前のスライドで見ていた関心領域が表示されます。

  • 左側には、ディスパッチ入力イベントが発生したことが表示されています。

  • それが状態を変更し、再コンポーズを引き起こした可能性があります。

  • しかし、これは不明です。

  • 確かなことはわからない。

  • しかし、再コンポーズ トレースでは、再コンポーズが発生した理由を正確に伝えるカスタム メタデータをトレースに追加します。

  • それだけでなく、これらのイベントが関連していることを簡単に把握できます。

  • クリックすると、どの状態が変更されたかが正確に表示されます。

  • これは状態であり、以前は true だったのが false に切り替えられた可変状態値であることがわかります。

  • それだけでなく、クラッシュの原因となった正確なコールスタックも表示されます。

  • ここでは、AbstractClickableNode.performClick が発生したことで、変更可能な状態が変化し、再コンポーズが発生したことがわかります。

  • これは本当に素晴らしいことで、これまでできなかったことです。

  • これらのツールは、新しい Android Performance Analyzer ツールでも利用できます。エージェント ツールを試してみたい場合は、そちらをご利用ください。

  • そして右。

  • コンテキストの伝播は Kotlin コルーチンで完全にサポートされていると説明しました。

  • しかし、コルーチンと非コルーチンのコードが混在しているコードや、独自にスレッドを実行しているコード、何らかの理由でコールバックを使用している古いブラウンフィールド コードがある場合はどうでしょうか。

  • 違いますね。

  • つまり、コンテキスト伝播のメリットをすべて享受できるということです。

  • たとえば、コールバックとエグゼキュータを使用するブラウンフィールド API があるとします。

  • この実装では、executor.execute を使用して runCatching(実装の詳細)を呼び出し、レスポンスを取得して、そのレスポンスをコールバックに通知します。

  • 違いますね。

  • これをトレースする場合は、Tracer.global.trace を実行してラベルを付け、ブロックを囲みます。

  • しかし、イベントの因果関係も保持したいので、プロパゲーション トークンと呼ばれる新しいオプション タイプを受け入れます。

  • そして、トークンを内部トレース API に渡します。

  • このメソッドを呼び出すユーザーは、必要に応じて伝播トークンを提供できるため、発生するイベントの因果関係を維持できます。

  • 違いますね。

  • この API の呼び出し元は、サスペンド API になります。

  • ここでは、suspend fun profileRequest があり、コールバックとエグゼキュータを使用する API を呼び出します。

  • ここでは、2 つの世界(suspend の世界とコールバックの世界)をブリッジするため、suspendCoroutine ビルダーを使用します。

  • 継続を取得し、API を呼び出して、レスポンスを受け取ったらコールバックで継続を再開します。

  • ここで定義しているエグゼキュータは profileRequestContext です。

  • これはシングルスレッド エグゼキュータです。

  • また、traceCoroutine はコルーチン コンテキスト内に伝播トークンを埋め込むため、コルーチン コンテキスト内から伝播トークンを取得し、ブラウンフィールド API への入力として渡すことで、イベントの因果関係を維持できます。

  • トレースでは、それが非常に明確になります。

  • この例では、worker-3 で開始された処理が、コンテキストの切り替え後にシングルスレッド エグゼキュータに切り替わり、その後 worker に戻って再開されています。

  • これは非常に便利です。

グローバル トレースとカスタム初期化

  • Rahul Ravikumar

    違いますね。

  • 他にも、Tracer.global をカスタマイズできます。

  • これまでのところ、Tracing 2.0 API には Tracer.global のデフォルト値として非常に適切なものが用意されていますが、必要に応じてカスタマイズすることもできます。

  • また、Tracing 2.0 は JVM と Android をサポートしています。これは、Java ライブラリと Android ライブラリの両方でトレースを完全にサポートするためです。

  • 初期化の 3 つのステップは、まずトレースシンクを作成します。

  • トレースシンクは、実質的にトレース パケットの保存方法です。

  • ワイヤ形式と、出力トレースが保存される場所を定義します。

  • Perfetto 形式をサポートするトレースシンクのすぐに使える実装が用意されていますが、独自のトレースシンクを作成することもできます。

  • これは、Tracing 1 で利用可能なバックエンドが atrace のみであったという問題を解決しようとしたことに起因します。

  • この点を修正したいと考えました。

  • そのため、OpenTelemetry シンクを作成したり、独自のシンクを使用したりすることも可能です。

  • この設定はオーバーライドできます。

  • シンクを作成したら、トレース ドライバを作成してシンクを渡します。

  • また、必要に応じて、有効にするカテゴリを選択することもできます。

  • ここでカテゴリが真価を発揮します。

  • そのため、リリースでは、デバッグと比較して、一部のカテゴリが他のカテゴリよりも重要であると判断し、それらのカテゴリを有効にすることができます。

  • または、「ソースに関係なく、すべてのカテゴリを有効にしたい」と言うこともできます。

  • まったく問題ありません。

  • トレースに対してグローバルなカスタム属性を追加することもできます。

  • そのため、ビルド バージョンやフレーバーなどの情報を埋め込むことができます。また、トレースに添付するその他のメタデータも、addAttribute API を使用して添付できます。

  • 最後に、setGlobalTracer を呼び出してトレーサーを登録します。

  • これにより、Tracer.global がこのトレーサーのインスタンスにバインドされ、その後、プロセスで使用できるようになります。

  • Android では、プロセスのライフサイクルが明確に定義されているため、AbstractTraceDriver.Factory を実装して create メソッドをオーバーライドするだけで、以前と同じ処理を行うことができます。

  • シンクを作成してから、ドライバを作成します。

  • ここで登録する必要はありません。

  • この問題が最初に発生したため、Google が登録を行います。

  • 違いますね。

  • トレースはマルチプラットフォームに対応しています。

  • これは意図的な選択です。Compose は複数のプラットフォームで実行されるため、できるだけ多くの場所でトレースできるようにしたいと考えています。

  • 現在は Android と JVM をサポートしていますが、この実装は、将来的に他のプラットフォームをサポートする場合にも完全に対応できます。

ビルドツールの最適化(R8 と metalava)

  • Rahul Ravikumar

    Android でソースコードの圧縮と最適化に使用されるツールである R8 は、Tracing 2.0 を使用しています。

  • トレースを活用することで、R8 を最適化し、コードの圧縮と最適化を 25% 高速化しました。

  • これにより、ビルド時間が短縮され、最適化パスの待ち時間も短縮されます。

  • R8 で行った最適化をすべて説明するわけではありませんが、トレースによって何が可能になるかを理解していただくために、1 つだけ説明します。

  • つまり、R8 は本質的に、ソースからソースへの変換ツールです。

  • つまり、多数の JAR を取得し、それらを圧縮して最適化し、dex 可能な別の JAR セットを生成します。

  • まず、AGP は JAR を読み取ります。これはアプリだけでなく、そのすべての依存関係も含まれます。

  • そのため、Gradle ワーカーを使用して、すべての入力 JAR の読み取りを開始します。

  • 読み取り対象の JAR が多数ある。

  • JAR の読み取りが完了すると、R8 はコードの解析を開始し、コードが表す内容とすべてのエントリ ポイントのモデルを構築して、コードの最適化と圧縮を開始します。

  • このスライス「await」は、R8 が最適化プロセスを実際に開始する準備が整った時点を表しています。

  • 中央にデッドスペースがあるように見えるかもしれません。なぜ未使用の CPU 時間があるのか疑問に思われるかもしれません。

  • その理由は、古い API の仕組みでは、すべてのワーカーがすべてのファイルを読み取るまで待ってから解析を開始していたためです。

  • もちろん、これは修正できます。実際、修正しました。

  • AGP 9 では、入力 JAR の読み取りが完了した時点で、すぐに処理が開始されていることがわかります。

  • また、すべてのワーカーが入力の読み取りを完了するまで待つ必要はありませんでした。

  • これにより、すべての入力を読み取るまでの待機時間が大幅に短縮されました。

  • これは、トレースを見ると非常に明白な最適化ですが、ソースコードだけを見て最適化する価値があるものを探している場合は、それほど明白ではありません。

  • AndroidX エンジニアが毎日使用するもう 1 つのツールは Metalava です。

  • Metalava は、Google が提供する API が、デベロッパーが期待する API 互換性を保証し、外部のユーザーが、Google が避けたいと考えているような問題に遭遇することなく、完全に利用できるようにするために使用するツールです。

  • このツールは、すべての公開 API と制限付き API で実行されるため、ここで節約した時間は、AndroidX エンジニアとして実際に取り戻せる時間となります。

  • Metalava もトレースを使用して、送信後の時間を 15% も短縮しました。

  • トレースを調べて何が起こったのかを説明します。Metalava は 2 種類のチェックを実行します。1 つ目は制限付き API のチェック、2 つ目は公開 API のチェックです。

  • しかし、あるパターンに気づくかもしれません。

  • これらのチェックを 2 回実行するために、さまざまな設定を行っています。

  • すべてのプログラム入力が読み取られ、PSI(プログラム構造インターフェース)が構築されます。これは、推論可能なコードの形状を正確に表したものです。

  • そして、AST をビルドしてから、必要な lint チェックをすべて適用します。

  • この高コストな処理を 2 回行っています。

  • この問題をどのように解決したか

  • 1 回だけ行いました。

  • つまり、すべての設定作業を 2 回行う代わりに、1 回だけ行い、その入力を制限付きチェックと公開チェックの両方で再利用することで、すべてのコストを削減しました。

  • うん

  • 違いますね。

トレースのベスト プラクティスとベンチマーク

  • Rahul Ravikumar

    Tracing 2.0 で何ができるかについて、少しお話ししました。トレースは重要ですが、何をトレースすべきかを知ることも同様に重要です。

  • トレースをあちこちに散りばめることは望ましくありません。

  • トレースする対象は慎重に選ぶ必要があります。ここでは、トレースする価値があるものについて、いくつかのガイドラインを示します。

  • そのため、最初に行うべきことは、トレースで説明できないギャップを見つけることです。

  • スレッドが実行中または実行可能な状態のときに、システム トレースに明らかなギャップが見られ、その理由がわからず、アプリが何をしているのかわからない場合は、調査する価値があるかもしれません。

  • その時点で、システム トレースに重ねてメソッド トレースを取得し、トレースする価値のあるものを確認して、関連するトレース セクションを追加するかもしれません。

  • また、ユーザー ジャーニーを介して優先順位を付けることも必要です。

  • そこで、アプリの起動を調べる必要があります。

  • アプリのコア ユーザー ジャーニーを調べて、トレースを見ることでそれらを説明できるかどうかを判断したいと考えています。

  • そのため、何かについて推論できない場合は、トレースする価値があります。

  • また、トレースから深いコールスタックや説明できない CPU 使用率を調べて、何が起きているのかを正確に把握し、コードベースのそれらの部分をトレースに変換する必要があります。

  • そのため、トレースできます。

  • また、スレッドがスリープ状態になっている場合は、その理由を把握できるようにする必要があります。

  • I/O を待機しているか、バインダー トランザクションを待機しているか、発生が予想される外部入力イベントを待機しているはずです。

  • しかし、説明のつかない睡眠がある場合は、明らかに何かが潜んでいる可能性があるため、調査する価値が非常に高いです。

  • そして、そのコードにすべてのトレース セクションを散りばめたら、それらをベンチマークに変換して、変更を追跡できるようにする必要があります。

  • コードベースが進化するにつれて、指標が変化しているかどうか、ダッシュボードに大きな変化が実際に反映されているかどうかを確認できます。これにより、重要な変化があったことがわかります。

  • または、これまでの手順をすべてスキップして、エージェントに任せることもできます。

  • これらのガイドラインをスキルにして、お気に入りのエージェントに適用し、実行させます。

  • うん

セットアップ、概要、まとめ

  • Rahul Ravikumar

    トレースが価値のある取り組みであり、実施すべきであると納得していただけたなら、プロジェクトのセットアップは次のようになります。

  • とても簡単です。

  • トレースは、従来の Java ライブラリです。androidx.tracing:tracing-wire への依存関係を定義します。これは、Perfetto 互換のトレース ドライバとトレースの組み合わせです。現在の安定版は 2.0.5 です。

  • Java ライブラリの場合もまったく同じです。これも完全にマルチプラットフォームの API です。

  • 今後、必要に応じて他のプラットフォームにも拡大していく予定です。

  • 簡単に復習しましょう。

  • ここまで、追加された優れた新機能について説明してきました。

  • コンテキストの伝播は非常に強力です。

  • カスタム情報、メタデータ、コールスタック情報など、あらゆる種類の情報を埋め込むことができます。

  • まだ話していないことがたくさんあります。

  • トレース形式を変更したり、トレースシンクを変更したり、トレースデータを OpenTelemetry に取り込んだりできます。

  • これらはすべて、お客様ご自身で行っていただけます。

  • トレースはマルチプラットフォームです。

  • これは、できるだけ多くの人にこの機能を利用してもらいたいという、非常に意識的な決断です。

  • Perfetto、AndroidX Benchmarks、新しいパフォーマンス アナライザ、古い Android Profiler など、Google が提供するすべてのツールが Tracing 2.0 をサポートしています。

  • ご視聴ありがとうございました。

  • デベロッパー サミットをお楽しみください。ありがとうございました。