パフォーマンスを測定する
プロファイラー、トレース、ベンチマークを使います。
「パフォーマンスを測定する」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。
最適化の前に計測する
パフォーマンス改善における黄金律は、まず計測し、決して推測しないことです。何が遅いかについての人間の直感は、ほとんどの場合、間違っています。
このレッスンでは、Androidが提供するパフォーマンスを可視化するツール、つまりプロファイラ、システムトレース、マイクロベンチマークについて学びます。計測できるようになれば、最適化の判断を勘ではなくデータに基づいて行えるようになります。
- プロファイラは、CPU、メモリ、エネルギーの使用状況をリアルタイムで表示します。
- システムトレースは、各フレームの時間が正確にどこで使われているかを明らかにします。
- ベンチマークは、ビルド間で比較できる再現性のある数値を提供します。
16ミリ秒のフレーム予算
60Hzの画面では、システムは16.67msごとに新しいフレームを描画します。アプリがその時間内にフレームを準備できないと、フレームが落ち、ユーザーにはjank(カクつき)として感じられます。120Hzのデバイスでは、予算は約8msまで縮まります。
パフォーマンス改善とは、実際にはこの予算内に収め続けることです。下のコメントには、常に頭の中で行っている計算が示されています。
// Frame budget math
// 60 Hz -> 1000ms / 60 = 16.67ms per frame
// 90 Hz -> 1000ms / 90 = 11.11ms per frame
// 120 Hz -> 1000ms / 120 = 8.33ms per frame
//
// Exceed the budget on the UI thread = a dropped frame = visible jank.
fun frameBudgetMs(refreshHz: Int): Double = 1000.0 / refreshHz
fun main() {
println("60Hz -> %.2f ms".format(frameBudgetMs(60)))
println("120Hz -> %.2f ms".format(frameBudgetMs(120)))
}Android Studio Profiler
Android Studio Profiler(View > Tool Windows > Profiler)は実行中のアプリに接続し、CPU、メモリ、電力、ネットワークのライブタイムラインを表示します。
- CPU:メソッドトレースとシステムトレースを記録して、負荷の高いコードパスを見つけます。
- Memory:割り当てを監視し、ヒープダンプを取得してリークを見つけます。
- Energy:バッテリーを消耗させるウェイクロックや過剰なジョブを見つけます。
必ず実機でリリース版に近いビルドをプロファイリングしてください。デバッグビルドやエミュレーターでは最適化が無効になっているため、数値が実態と異なります。
実際のシナリオ向けのMacrobenchmark
Jetpack Macrobenchmarkライブラリは、実機上でユーザー操作全体(起動、スクロール、画面遷移)を測定し、フレーム時間や起動時間などの安定した指標を報告します。
アプリを起動して一連の操作を実行するテストを作成すると、ライブラリがそれを何度も実行し、中央値と99パーセンタイルの値を示します。
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test
fun coldStartup() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
pressHome()
startActivityAndWait()
}
}負荷の高いコード向けのMicrobenchmark
1つの関数がどれほど高速に動作するかを知りたい場合は、Microbenchmarkライブラリを使用します。コードを狭いループ内で実行し、JITをウォームアップしたうえで、ノイズを取り除いた1操作あたりのナノ秒数を報告します。
パーサー、シリアライザー、ソート、または頻繁に呼び出すCPU負荷の高いヘルパーに使用してください。
@RunWith(AndroidJUnit4::class)
class JsonParseBenchmark {
@get:Rule val benchmarkRule = BenchmarkRule()
@Test
fun parseLargePayload() {
val raw = loadSampleJson()
benchmarkRule.measureRepeated {
val parsed = parseUsers(raw) // code under test
// assertEquals avoids the compiler dropping the result
assertTrue(parsed.isNotEmpty())
}
}
}Perfettoによるシステムトレース
システムトレースは、すべてのスレッドとシステムがフレームごとに何を行ったかを記録します。Android StudioのCPUプロファイラーで記録できるほか、adbで取得してPerfetto(ui.perfetto.dev)で開くこともできます。
トレースには、問題のある赤いフレーム、メインスレッドで行われた処理、GPUで費やされた時間が表示されます。jankの原因を診断するための、最も優れたツールです。
# Record a 5-second system trace from the command line
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace \
-t 5s sched freq idle am wm gfx view
# Pull it to your machine, then open in https://ui.perfetto.dev
adb pull /data/misc/perfetto-traces/trace.perfetto-traceカスタムトレースセクション
標準状態では、トレースにフレームワークの処理が表示されます。自分のコードにラベルを付けるには、名前付きのトレースセクションで囲みます。これらの名前はPerfettoでラベル付きブロックとして表示されるため、負荷の高い箇所を簡単に見つけられます。
androidx.tracing APIを使用すると、デバッグトレースとベンチマークトレースの両方にセクションが表示されます。
import androidx.tracing.trace
fun loadDashboard(repo: Repo): Dashboard {
return trace("loadDashboard") {
val user = trace("fetchUser") { repo.user() }
val feed = trace("fetchFeed") { repo.feed() }
Dashboard(user, feed)
}
}
// In Perfetto you will now see named slices:
// loadDashboard > fetchUser, fetchFeed実行時のドロップフレームの追跡
常にプロファイラーを接続できるとは限りません。JankStatsは、実行中のアプリ内部からjankyなフレームを報告するJetpackライブラリです。そのため、フレームをログに記録したり、集計値を分析基盤に送信したりできます。
ウィンドウにフックし、フレームが予算を超えるたびにコールバックを呼び出します。
val jankStats = JankStats.createAndTrack(window) { frameData ->
if (frameData.isJank) {
Log.w("Jank", "Janky frame: ${frameData.frameDurationUiNanos} ns")
analytics.logJank(frameData.frameDurationUiNanos)
}
}
// Pause/resume with your screen lifecycle
override fun onResume() { super.onResume(); jankStats.isTrackingEnabled = true }
override fun onPause() { super.onPause(); jankStats.isTrackingEnabled = false }数値の読み方:パーセンタイル
単一の平均値では、問題の深刻さが隠れてしまいます。平均フレーム時間が10msでも、99パーセンタイルが40msなら、フレームの1%がjankyであり、ユーザーはそれを感じます。平均値だけでなく、常にP50、P90、P99を確認してください。
ベンチマークはこれらの値を報告してくれます。下のヘルパーは、生のフレームサンプルを使ってその考え方を示しています。
fun percentile(samplesMs: List<Double>, p: Int): Double {
val sorted = samplesMs.sorted()
val index = ((p / 100.0) * (sorted.size - 1)).toInt()
return sorted[index]
}
fun main() {
val frames = listOf(8.0, 9.0, 10.0, 9.5, 11.0, 40.0, 9.0, 10.5)
println("P50 = %.1f ms".format(percentile(frames, 50)))
println("P99 = %.1f ms".format(percentile(frames, 99)))
}再現可能な測定ワークフロー
信頼できる数値は、規律あるプロセスから得られます。毎回、次のチェックリストに従ってください。
- release / non-debuggableビルド(R8有効)を使用します。
- 安定したサーマル状態の実機で、電源に接続して実行します。
- シナリオを数回繰り返し、中央値を報告します。
- 一度に1つのことだけ変更して、再測定します。
- 保存したベースラインと比較し、改善を証明できるようにします。
これらを省くと、実際の問題を修正する代わりにノイズを追いかけることになります。
時間は実際にどこで使われているのか
Androidのjankの多くは、限られた原因から生じます。原因を知っていれば、どこにツールを使うべきか判断できます。
- メインスレッドの処理:UIスレッド上でのディスク処理、ネットワーク処理、JSON処理です。
- 過剰な再コンポジション:Composeが必要以上に再描画しています。
- 過剰なメモリ割り当て:ガベージコレクションによる一時停止です。
- レイアウトパス:深くネストされたレイアウトや、繰り返し測定されるレイアウトです。
このコースの次のレッスンでは、再コンポジション、メモリリーク、起動を直接扱います。最初にどれを修正すべきかを教えてくれるのが、測定です。
クイックチェック
実機でアプリのコールドスタートアップにかかる時間を、複数回の実行にわたって安定かつ再現可能な形で測定したいとします。最適なツールはどれでしょうか。
まとめ:これで測定できるようになりました
変更を加える前に、パフォーマンスを可視化する方法を学びました。
- 16msのフレーム予算が、滑らかな動作とjankyな動作の境界を定めます。
- Android Studio Profilerは、CPU、メモリ、電力をリアルタイムで表示します。
- MacrobenchmarkとMicrobenchmarkは、再現可能な数値を提供します。
- Perfettoのトレースとカスタム
trace()セクションによって、時間がどこで使われているかがわかります。 - JankStatsは、実行中のアプリ内部からjankを報告します。
- P50/P90/P99を読み、実機でリリースビルドをプロファイリングし、一度に1つのことだけ変更します。
次は、Composeのjankの大きな原因の1つである再コンポジションに、これらを適用します。
よくある質問
「パフォーマンスを測定する」レッスンは無料ですか?
はい。「パフォーマンスを測定する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。
「パフォーマンスを測定する」で何を学びますか?
プロファイラー、トレース、ベンチマークを使います。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Android Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「パフォーマンスを測定する」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAndroid Academyレッスンでコードを書いて実行できますか?
はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- パフォーマンスを測定する
- 再コンポジションを制御する
- メモリリークと修正方法
- 起動処理とBaseline Profiles