以前 LT で発表した記事のテキスト化 & 補足です。
Baseline Profiles はアプリのパフォーマンス改善を見込めるものになっているので、ぜひ検証 & 導入を進めていきたいですね。
Baseline Profiles とは?
- ART (Android Runtime) がプリコンパイルするときに使用するアプリのクラスとメソッドのリスト
- Baseline Profiles を使うことでアプリの起動時間の短縮、ジャンクの削減、全体的なパフォーマンスの向上が見込める
- Android 7.0 ~ 最新 OS までに効果がある
- アプリのインストール後やアップデート後の初回起動はプリコンパイルされていない状態のため、特にアプリの起動速度やパフォーマンスが落ちる
なぜ Baseline Profiles が必要なのか
- これまで Cloud Profile として Google Play が同じような仕組みを提供しているが、こちらは提供されるまで時間がかかるためにリリーススパンが短いアプリでは恩恵を受けることができない
- Cloud Profile は実際にアプリをダウンロードしたユーザが起動した時の情報を Play 側で収集して Profile を作成しているので、Profile が生成されるまでタイムラグが発生する
- 頻繁にリリースがあるアプリだとその生成が間に合わない
- OS で最適化がされる Android View とは異なり、Jetpack Compose は OS にバンドルされないライブラリであるため最適化の恩恵を受けずにパフォーマンスの問題がある
- Jetpack Compose のライブラリ自体にも Baseline Profiles のルールが含まれている
- Android View は Zygote プロセスの仕組みで View 関連のクラスと Drawable がプリロードされている最適化が行われている
Baseline Profiles の導入
app モジュールに ProfileInstaller の依存を追加する
dependencies {
implementation("androidx.profileinstaller:profileinstaller:1.2.0")
}
Macrobenchmark のモジュールをアプリに追加する
- Android Studio から "New Module..." よりベンチマーク用のモジュールを追加するテンプレートが用意されている
- 注意点
-
productFlavors
が app モジュールにある場合は benchmark のモジュールでも以下のうちどれかの対応をする必要がある- app モジュールの
productFlavors
を benchmark モジュールにも同様の定義する -
matchingFallbacks
を使って新規の flavor を追加する -
missingDimensionStrategy
を使用する
- app モジュールの
-
Baseline Profiles を生成するテストの追加
@ExperimentalBaselineProfilesApi
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
pressHome()
startActivityAndWait()
}
}
Baseline Profiles を生成するテストをエミュレータで実行
- Google API かつ Android 9 ~ 12 のエミュレータを作成する
- テストの実行前に
adb root
で root 化しておく必要がある - Baseline Profiles を生成する buildType では難読化されない設定が必要
proguard-benchmark.pro
# When generating the baseline profile we want the proper names of
# the methods and classes
-dontobfuscate
app/build.gradle
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
benchmark {
initWith buildTypes.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-benchmark.pro'
signingConfig signingConfigs.debug
matchingFallbacks = ['release']
}
}
- テストを実行するのと同様に Baseline Profiles を生成するテストを実行
生成された Baseline Profiles をエミュレータから pull する
- Baseline Profiles 生成のテスト結果に Baseline Profiles のファイルをエミュレータから取り出すコマンドが表示されるのでそれを実行する
- 取得したファイルを
baseline-prof.txt
の名前で app/src/main 以下に配置する
Macrobenchmark でアプリの起動時間が改善されるか確認
- デバッグビルドやリリースビルドでのアプリのインストールでは Baseline Profiles は働かないので、Macrobenchmark で実際に改善されるのかを確認する必要がある
- 計測には物理デバイスでの確認が公式ドキュメントで推奨されている
StartupBenchmark.kt
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun startupNoCompilation() {
startup(CompilationMode.None())
}
@Test
fun startupBaselineProfile() {
startup(
CompilationMode.Partial(
baselineProfileMode = BaselineProfileMode.Require
)
)
}
private fun startup(compilationMode: CompilationMode) {
benchmarkRule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD,
compilationMode = compilationMode
) {
pressHome()
startActivityAndWait()
}
}
}
-
CompilationMode.None
は Baseline Profiles を無効にした状態でのテストになり、アプリ起動時に ART が都度コンパイルしてアプリを起動させる- このテストでは Play Store からアプリをインストールやアップデートした後の初回起動の状態を再現している
-
CompilationMode.Partial
は Baseline Profiles のみを利用してプリコンパイルされた状態でのテストになる- このテストでは Play Store からアプリをインストールやアップデートした後の初回起動の状態で Baseline Profiles が適用された状態を再現している
- 上記は個人のプロジェクトである https://github.com/NUmeroAndDev/MaterialGallery-android で導入した結果
- Android View のプロジェクトで、アプリ起動時の処理が少ないプロジェクトのため多少の改善程度になっている
- Jetpack Compose の場合は Jetpack Compose のライブラリに Profiles のルールが仕組まれているため
CompilationMode.None
でのテストはかなりパフォーマンスが落ちる結果が出てくる