auditing-compose-performance について
Android 開発者向けの Compose パフォーマンス監査スキルです。測定、診断、修正、検証の順に進め、最後に監査レポートを作成します。
主な機能
- 014 段階の監査ワークフロー
- 02実機での性能測定
- 03コンパイラと実行時の診断
- 04修正を一つずつ適用
- 05ベースラインと CI の安定性ゲート
- 06監査レポートの作成
活用シーン
- 01アプリ全体の動作の重さを調査
- 02性能改善スプリントの計画
- 03リリース前の性能ゲート設定
- 04性能課題のあるコードベースへの新人導入
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ skydoves/ compose-performance-skills --skill auditing-compose-performanceインストール後、Claude Code で/auditing-compose-performanceを入力して読み込まれたか確認します。
詳細
Compose パフォーマンスの監査 — Measure → Diagnose → Fix → Verify オーケストレーター
これは compose-performance-skills ライブラリの最上位のエントリーポイントです。開発者が「アプリの動作がもたつく」「どこをスクロールしてもカクつく」「パフォーマンス改善のスプリントを始める」「そもそも何から始めればいいのか」といった漠然とした症状を抱えている場合、Claude はここから開始します。このオーケストレーターは、25 個の個別スキルに取って代わるものではありません。4 つのフェーズからなるループを通じてそれらを順に実行し、最後に監査レポートを文書として作成します。
フェーズは Measure → Diagnose → Fix → Verify の順に実施し、決してスキップしません。フェーズ 1 では、実機での release + R8 ビルドからベースラインの数値を取得します。それ以外は現実を反映しないからです。フェーズ 2 では、症状を Compose Compiler のレポート、Layout Inspector、ランタイムトレースから特定した原因に結びつけます。フェーズ 3 では、一度に 1 つの対象を絞った修正を適用し、各修正の間に再計測することで、それぞれの変更による差分を証明できるようにします。フェーズ 4 では、Baseline Profile を再生成し、CI の安定性ゲートを設定して、ベースラインファイルをコミットすることで、パフォーマンスのリグレッションが再発しないようにします。
計測を伴わないパフォーマンス改善は当てずっぽうです。Skydoves の注目すべき見解その 1 は全体を通して当てはまります。100% のスキップ可能性を追い求めてはいけません。それはコンパイラーレポートの診断項目であり、目標ではありません。目標は、実機における FrameTimingMetric と StartupTimingMetric の改善です。
このスキルを使用する場面
- 開発者が、具体的な画面を特定せずに全般的な動作の遅さを報告している場合(「アプリが重く感じる」「すべてが遅い」)。
- チームがパフォーマンス改善のスプリントを開始し、構造化された計画を求めている場合。
- パフォーマンスに問題のあるコードベースについて、新しいチームメンバーへのオンボーディングが必要な場合。
- リリース前にパフォーマンスのゲートが必要な場合。
- ユーザーが「監査」「パフォーマンスレビュー」、または文書化された成果物を求めている場合。
- ユーザーが何から始めればよいかわからず、Claude にトリアージを求めている場合。
このスキルを使用しない場面
- 具体的な症状がすでに特定されている場合(
LazyColumnのスクロール時のカクつき、derivedStateOfが実行されない、カスタムモディファイアが再コンポーズするなど)。該当する個別スキルに直接進んでください。症状からスキルを引く対応表は../../INDEX.mdを参照してください。 - 開発者が 1 つの問題だけを修正したいと考えており、文書化されたレポートを求めていない場合。
../../INDEX.mdから該当する個別スキルを選んでください。 - release ビルドができない場合(署名設定の破損など)。まずその問題を解決してください。この監査では release の計測が必須です。
前提条件
- プロジェクトが release バリアントを正常にビルドできること(
./gradlew assembleRelease)。 - 計測用の実機 Android デバイスが少なくとも 1 台あること。エミュレーターの数値は実機を反映しません。
- Macrobenchmark モジュールが存在すること、または追加する意思があること(
../../measurement/generating-baseline-profiles/SKILL.mdを参照)。 - Baseline Profile と安定性ベースラインファイルをリポジトリにコミットする意思があること。
stabilityConfigurationFileには Compose Compiler 1.5.5 以降が必要です。Strong Skipping のデフォルト有効化には Kotlin 2.0.20 以降が必要です。Baseline Profile Generator テンプレートには AGP 8.2 以降が必要です。- 任意ですが、各修正を個別の PR として取り込むためのフィーチャーブランチを推奨します。
ワークフロー
4 つのフェーズをすべて順番に実行してください。先のフェーズに進んでスキップしてはいけません。フェーズ 3 で修正するたびに、次の修正を適用する前にフェーズ 1 に戻って再計測し、フェーズ 2 で再診断してください。
フェーズ 1 — 測定(コードを変更する前にベースラインの数値を確立します)
- リリースバリアントがビルドされ、測定対象であることを確認します。
../../measurement/testing-compose-in-release-mode/SKILL.mdへの相互リンクを参照してください。 - R8 が正しく有効になっていること(フルモード、
proguard-android-optimize.txt、リソースの縮小が有効)を確認します。../../build/configuring-r8-for-compose/SKILL.mdへの相互リンクを参照してください。 - Baseline Profile Generator モジュールを使って、Baseline Profile を生成または更新します。
../../measurement/generating-baseline-profiles/SKILL.mdへの相互リンクを参照してください。 -
CompilationMode.Partial(BaselineProfileMode.Require)のもとで、MacrobenchmarkRuleとStartupTimingMetricを使用してコールドスタートアップの数値を取得します。10 回以上実行します。 - 対象の画面のスクロールの数値を
FrameTimingMetric(P50、P90、P99)で取得します。同じデバイスで 5 回以上実行します。 - すべての数値を監査レポートの「Baseline (Phase 1)」セクションに記録します。コードを変更する前に必ず実施してください。
// Phase 1 baseline scroll measurement (record P50/P90/P99 of frameDurationCpuMs)
@Test fun feedScroll() = rule.measureRepeated(
packageName = "com.example",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
startupMode = StartupMode.WARM,
compilationMode = CompilationMode.Partial(BaselineProfileMode.Require),
) {
startActivityAndWait()
device.findObject(By.res("feed")).fling(Direction.DOWN)
}
フェーズ 2 — 診断(症状を具体的な原因に結び付けます)
- リリースバリアントの Compose Compiler レポートを有効にします。
../../stability/diagnosing-compose-stability/SKILL.mdへの相互リンクを参照してください。 -
<module>-composables.txtを読みます。再起動可能だがスキップ不可のコンポーザブルをすべて列挙し、スキップを妨げている不安定なパラメーターを記載します。 -
<module>-classes.txtを読みます。不安定なクラスをすべて列挙し、問題のあるフィールド(var、不安定なフィールド型、インターフェースなど)を記載します。 - 不自然な判定(
runtime、unknown、「安定しているように見えるのにコンパイラーの判定は異なる」)については、../../stability/understanding-stability-inference/SKILL.mdを実行して、12 フェーズのアルゴリズムをたどります。 - 対象の画面で Layout Inspector を使い、再コンポジション数とスキップ数を有効にします。回数が多く、スキップが少ないホットスポットを特定します。
../../recomposition/debugging-recompositions/SKILL.mdへの相互リンクを参照してください。 - リリースビルドに Layout Inspector を適用できない場合は、リリース品質のトレースを行うため、上位のホットスポットに
@TraceRecompositionを追加して計測します。../../measurement/tracing-recompositions-at-runtime/SKILL.mdへの相互リンクを参照してください。 - 優先順位を付けます。問題を発生頻度 × コストでランク付けします。ホットな
LazyColumnの行で発生する再コンポジションは、設定画面で一度だけ発生する再コンポジションの 10 倍を上回ります。コンパイラーレポートの重大度だけでランク付けしないでください。 - 監査レポートの「Diagnosis」セクションを作成します。上位 5 件のホットスポット、再起動可能だがスキップ不可のコンポーザブル数、不安定なクラス数、フェーズが不適切な読み取りの数を記載します。
Phase 3 — 修正(対象を絞り、差分を最小限に抑えて変更する — PR ごとに原因は 1 つ)
優先順位を付けた各問題について、該当する専門スキルを選んで修正します。推奨: スキルごとに PR を 1 つ作成し、差分をレビューしやすく、二分探索しやすくします。修正するたびに Phase 1(測定)と Phase 2(診断)を再実行し、変更によって適切な指標が改善し、別の箇所にリグレッションが発生していないことを確認します。
- 安定性の修正(不安定な
data class、List/Set/Map型のパラメーター、java.time.LocalDateTime)→../../stability/stabilizing-compose-types/SKILL.md。 - Strong Skipping の監査(モードが有効であることを確認し、
@DontMemoizeまたは@NonSkippableComposableが必要なラムダのキャプチャ箇所を特定する)→../../recomposition/using-strong-skipping-correctly/SKILL.md。 - アニメーションとスクロールのフェーズ遅延修正(
Modifier.offset { }、Modifier.graphicsLayer { }、Modifier.drawBehind { })→../../recomposition/deferring-state-reads/SKILL.md。 -
derivedStateOfの誤用(rememberの欠落、状態ではない変数のキャプチャ、入力頻度が出力頻度を上回らない箇所での使用)→../../recomposition/choosing-derivedstateof/SKILL.md。 -
LazyColumn/LazyRow/LazyVerticalGridの Lazy レイアウトでのkeyとcontentTypeの指定、およびitemsラムダの外への修飾子チェーンのホイスト →../../lists/optimizing-lazy-layouts/SKILL.md。 - Compose Foundation 1.10 以降のデフォルト設定でも、高速スクロール時にフレームが落ちる場合のみ、Lazy プリフェッチを調整する →
../../lists/configuring-lazy-prefetch/SKILL.md。 - カスタム修飾子を
Modifier.composed { }からModifier.Node+ModifierNodeElementに移行する →../../modifiers/migrating-to-modifier-node/SKILL.md。 - 修飾子の順序に関するバグ(背景が誤った領域に描画される、クリック領域が表示上のボタンより広い、
backgroundの後にclipがある)→../../modifiers/ordering-modifier-chains/SKILL.md。 - Flow の安全な収集(
collectAsStateからcollectAsStateWithLifecycleへの変更、Flow<T>パラメーターをコンポーザブルの外にホイスト、.conflate()/.distinctUntilChanged()の追加)→../../side-effects/collecting-flows-safely/SKILL.md。 - Effect API の監査(
LaunchedEffectとRememberedEffect、DisposableEffect、SideEffectの使い分け、rememberUpdatedStateによる古いコールバックへの対処)→../../side-effects/using-efficient-effects/SKILL.md。 - 各修正の後、Phase 1 + Phase 2 を再実行し、監査レポートの Phase 3 の表に Macrobenchmark の差分を記録します。
Phase 4 — 検証(結果を確定する)
- Baseline Profile を再生成し、改善されたコードパスを反映します。
../../measurement/generating-baseline-profiles/SKILL.mdへの相互リンクを追加します。 - Macrobenchmark の数値を Phase 1 のベースラインと比較します。P50/P90/P99 の差分を「Verification (Phase 4)」セクションに記録します。
- リグレッションがあればビルドを失敗させる CI 安定性ゲートを設定します(
stabilityDumpは 1 回実行し、その後はすべての PR でstabilityCheckを実行します)。../../stability/enforcing-stability-in-ci/SKILL.mdへの相互リンクを追加します。 -
app/src/main/generated/baselineProfiles/baseline-prof.txt(または利用側モジュールが配置した場所)をコミットします。 -
:stabilityDumpで生成されたapp/stability/*.stabilityのベースラインファイルをコミットします。 - 監査レポート(以下のテンプレート)を作成し、共有します。書面によるレポートなしに監査完了を宣言してはなりません。
監査レポートのテンプレート
Claude は、監査の最後にこのファイルを必ず作成する必要があります。docs/perf-audit-<date>-<module>.md(またはプロジェクトでレポートを保存している場所)に保存します。
# Compose Performance Audit — <date> — <module>
## Environment
- Compose UI: <version>
- Compose Compiler: <version>
- Kotlin: <version>
- AGP: <version>
- Device: <model> / <API>
## Baseline (Phase 1)
- Cold startup median: <ms>
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms>
- Baseline Profile present: yes/no
## Diagnosis (Phase 2)
- Restartable-not-skippable composables: <count>
- Unstable classes: <count>
- Top 5 recomposition hotspots: <list>
- Phase-misplaced reads: <count>
## Fixes applied (Phase 3)
| Skill | Change | Files | Macrobench delta |
| ----- | ------ | ----- | ---------------- |
| stability/stabilizing-compose-types | wrap List<Snack> with ImmutableList | feed/SnackList.kt | scroll P90 18ms → 12ms |
| recomposition/deferring-state-reads | offset(x.dp) → offset { } | hero/Hero.kt | scroll P99 33ms → 19ms |
| ... | ... | ... | ... |
## Verification (Phase 4)
- Cold startup median: <ms> (Δ <ms>)
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms> (Δ ...)
- Baseline Profile regenerated: yes
- CI stability gate active: yes / no
## Open items / follow-ups
- <list>
パターン
パターン:1 行でも変更する前に測定する
// WRONG
// "Scroll feels rough — let me wrap this List in ImmutableList and add @Immutable everywhere."
// WRONG because: no baseline number exists, so any later claim of improvement is unfalsifiable.
// RIGHT
// 1. Run MacrobenchmarkRule on the suspect surface. Record P50/P90/P99 in the report.
// 2. Read the Compose Compiler report for the same surface. Identify the named cause.
// 3. Apply ONE targeted fix from a sibling skill.
// 4. Re-run the same Macrobenchmark. Compute the delta. Record it.
パターン:PR ごとに原因は 1 つ
WRONG: a single PR titled "perf improvements" that
- converts 4 data classes to ImmutableList parameters
- migrates 2 custom modifiers to Modifier.Node
- moves 3 graphicsLayer reads down a phase
- adds a Baseline Profile module
WRONG because: if the Macrobench delta is mixed (startup faster, scroll slower), the
audit cannot attribute the regression to a specific change. Bisect impossible.
RIGHT: four PRs, each scoped to a single skill, each with its own Before/After
Macrobench number in the description. The audit report links to each PR in the
Phase 3 table.
パターン:スキップ可能性は診断指標であり、KPI ではない
WRONG: "We got composables.txt skip rate from 71% to 100%. Audit complete."
WRONG because: skippability is a means. The end is FrameTimingMetric and
StartupTimingMetric improvement on a real device. A 100% skippable app can still
drop frames if the work happens in Layout or Draw.
RIGHT: "Scroll P90 dropped from 18ms to 11ms. Cold startup median dropped from
820ms to 610ms. Skip rate improved as a side effect; we did not target it directly."
Compose のバージョン要件の付録
| 機能 | 最小バージョン |
|---|---|
| Strong Skipping のデフォルトが ON | Kotlin 2.0.20+ |
LazyLayoutCacheWindow | Compose Foundation 1.9+ |
| 遅延プリフェッチでの一時停止可能なコンポジション(デフォルト) | Compose Foundation 1.10+ |
stabilityConfigurationFile DSL | Compose Compiler 1.5.5+ |
| Baseline Profile Generator テンプレート | AGP 8.2+ |
| R8 フルモードのデフォルト化 | AGP 8.0+ |
Modifier.animateItem() の GA | Compose UI 1.7+ |
rememberGraphicsLayer() | Compose UI 1.7+ |
必須ルール
- 必須: 4 つのフェーズをすべて、Measure → Diagnose → Fix → Verify の順に完了してください。先に進んで飛ばしてはいけません。
- 必須: コードを変更する前に数値を記録してください。そうしないと、改善したという主張を検証できません。
- 必須: Phase 3 の各修正は名前を付けた原因 1 つに限定し、修正ごとに再測定してください。これにより、各変更による差分を証明できます。
- 必須: Phase 4 の最後に、再生成した Baseline Profile と stability baseline ファイルをコミットしてください。
- 必須: release + R8 と実機で測定してください。デバッグ時やエミュレーターでの数値は参考になりません。
- 禁止: 上記のテンプレートに沿ったレポートを作成せずに、監査が完了したと宣言してはいけません。
- 禁止: すべての処理をスキップ可能にすることを目指してはいけません(skydoves hot take #1)。目標は
FrameTimingMetricとStartupTimingMetricの改善であり、コンパイラーレポート上の指標ではありません。 - 禁止: 対象を絞ったスキルの修正を適用する前に、そのスキルの前提条件と検証チェックリストを読まずに進めてはいけません。
- 推奨: 修正スキルごとに PR を 1 つ作成してください。差分が小さければレビューや原因の切り分けが容易になります。
- 推奨: Phase 2 のホットスポットは、コンパイラーレポート上の深刻度ではなく、発生頻度 × コストの順で順位付けしてください。
検証
- Phase 1 の数値(コールドスタートアップの中央値、スクロール時の FrameTimingMetric P50/P90/P99)が、コードを変更する前にレポートへ記録されている。
- Phase 2 の診断が記述されている:restartable-not-skippable の数、不安定なクラスの数、上位 5 件のホットスポットの順位。
- Phase 3 の各修正について、適用した具体的なスキル、変更したファイル、測定可能な Macrobenchmark の差分が、レポートの個別の行に記録されている。
- Phase 4:Baseline Profile を再生成してコミット済みである。
app/stability/*.stabilityファイルをコミット済みである。CI のstabilityCheckゲートが有効である。 - 監査レポートが保存され、(例:
docs/perf-audit-<date>-<module>.md)関係者に共有されている。 - コールドスタートアップの中央値とスクロール時の P90 がともに改善しているか、悪化せず維持されている。いずれかが悪化した場合は、レポートの「Open items」セクションに原因と次の対応が記載されている。
参考資料
関連スキル(このオーケストレーターが順に実行する、対象を絞った 25 件のスキル)
Phase 1 — Measure:
../../measurement/testing-compose-in-release-mode/SKILL.md— デバッグビルドの数値は当てになりません。release + R8 で測定してください。../../measurement/generating-baseline-profiles/SKILL.md— Baseline Profile Generator と Macrobenchmark を最初から最後まで実行します。../../build/configuring-r8-for-compose/SKILL.md— フルモード、ProGuard ファイルの最適化、コンシューマールールの信頼性について説明します。 フェーズ 2 — 診断:../../stability/diagnosing-compose-stability/SKILL.md— Compose Compiler レポートを有効化して読み取ります。../../stability/understanding-stability-inference/SKILL.md— 12 フェーズのアルゴリズム、$stableフィールド、ジェネリックビットマスク。../../stability/using-stability-analyzer-ide-plugin/SKILL.md— インラインで安定性を検査するための IDE プラグイン。../../stability/visualizing-recomposition-cascades/SKILL.md— 不安定なパラメータがコンポーザブルツリーを通じてどのように伝播するかを可視化します。../../recomposition/debugging-recompositions/SKILL.md— Layout Inspector のカウントと Argument Change Reasons。../../measurement/tracing-recompositions-at-runtime/SKILL.md— リリースグレードのトレースのための@TraceRecomposition。
フェーズ 3 — 修正:
../../stability/stabilizing-compose-types/SKILL.md— 3 段階のウォーターフォール: 書き換え、アノテーション、設定。../../recomposition/using-strong-skipping-correctly/SKILL.md— モードの検証、ラムダキャプチャ箇所の監査、エスケープハッチ。../../recomposition/deferring-state-reads/SKILL.md— ラムダモディファイアを介して読み取りを Layout/Draw まで押し下げます。../../recomposition/choosing-derivedstateof/SKILL.md— 入力頻度が出力頻度を上回る場合にのみ使用します。../../recomposition/avoiding-subcomposition-pitfalls/SKILL.md—SubcomposeLayout/BoxWithConstraintsのコストが利点を上回る状況を認識します。../../lists/optimizing-lazy-layouts/SKILL.md—key、contentType、Modifier.animateItem()、ホイスティング。../../lists/configuring-lazy-prefetch/SKILL.md—LazyLayoutCacheWindow、一時停止可能なプリフェッチ、NestedPrefetchScope。../../modifiers/migrating-to-modifier-node/SKILL.md—composed { }の代わりにModifier.Node+ModifierNodeElementを使用します。../../modifiers/ordering-modifier-chains/SKILL.md— 「次のモディファイアをラップする」というメンタルモデル。../../side-effects/collecting-flows-safely/SKILL.md—collectAsStateWithLifecycle、Flow<T>パラメータのホイスティング。../../side-effects/using-efficient-effects/SKILL.md— 最も低コストで正確な effect API を選択します。
フェーズ 4 — 検証:
../../stability/enforcing-stability-in-ci/SKILL.md—stabilityDumpベースライン +stabilityCheckCI ゲート。../../measurement/generating-baseline-profiles/SKILL.md— 修正後に再生成し、baseline-prof.txtをコミットします。 ホットリロード (開発者ループの速度、オプションですが推奨):../../hot-reload/setting-up-compose-hotswan/SKILL.md— 秒未満のイテレーションのために HotSwan をインストールして設定します。../../hot-reload/preserving-state-across-reloads/SKILL.md— リロードをまたいでremember/rememberSaveableの状態を保持します。../../hot-reload/understanding-hot-reload-limits/SKILL.md— クリーンにリロードできるものと、完全な再ビルドを強制するもの。../../hot-reload/iterating-with-ai-and-mcp/SKILL.md— タイトなループのために HotSwan を MCP 対応の AI ツールと組み合わせます。
症状と API の検索: ../../INDEX.md。
外部リファレンス
- Android Developers — パフォーマンスの概要: https://developer.android.com/develop/ui/compose/performance
- Android Developers — 安定性の概要: https://developer.android.com/develop/ui/compose/performance/stability
- Android Developers — フェーズとパフォーマンス: https://developer.android.com/develop/ui/compose/performance/phases
- Android Developers — Baseline Profiles: https://developer.android.com/develop/ui/compose/performance/baseline-profiles
- Ben Trengrove — リリースでパフォーマンスをテストすべき理由: https://medium.com/androiddevelopers/why-should-you-always-test-compose-performance-in-release-4168dd0f2c71
- Skydoves — 「アプリのパフォーマンスを最適化する 6 つの Jetpack Compose ガイドライン」: https://medium.com/proandroiddev/6-jetpack-compose-guidelines-to-optimize-your-app-performance-be18533721f9
- Skydoves — 「安定性をマスターしてアプリのパフォーマンスを最適化する」: https://medium.com/proandroiddev/optimize-app-performance-by-mastering-stability-in-jetpack-compose-69f40a8c785d
- Skydoves — compose-performance ハブ: https://github.com/skydoves/compose-performance
- Chris Banes — Composable メトリクス: https://chrisbanes.me/posts/composable-metrics/