Files
2nd/10_Wiki/Topics/Coding/Android_Baseline_Profile.md
T
2026-05-09 21:08:02 +09:00

304 lines
7.7 KiB
Markdown

---
id: android-baseline-profile
title: Android Baseline Profile — Startup / Scroll 최적화
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [android, performance, baseline-profile, vibe-coding]
tech_stack: { language: "Kotlin / Macrobenchmark", applicable_to: ["Android"] }
applied_in: []
aliases: [Baseline Profile, AOT, Macrobenchmark, startup metric, frame timing]
---
# Android Baseline Profile
> Startup / scroll 30% 빠르게. **Macrobenchmark 가 critical user flow trace → AOT compiled**. R8 + Compose 와 결합.
## 📖 핵심 개념
- Baseline Profile: 자주 쓰는 코드 path 미리 AOT compile.
- Macrobenchmark: 측정 + profile 생성.
- Default profile: Compose / 일부 lib 자동.
- Startup Profile: cold start 가속.
## 💻 코드 패턴
### Setup
```groovy
// build.gradle (app)
plugins {
id 'androidx.baselineprofile'
}
dependencies {
implementation 'androidx.profileinstaller:profileinstaller:1.3.1'
baselineProfile project(':baselineprofile')
}
baselineProfile {
saveInSrc = true // src/main/baseline-prof.txt
automaticGenerationDuringBuild = false // CI 에서만
}
```
### baselineprofile module
```groovy
// :baselineprofile/build.gradle
plugins {
id 'com.android.test'
id 'androidx.baselineprofile'
}
android {
targetProjectPath = ':app'
}
dependencies {
implementation 'androidx.benchmark:benchmark-macro-junit4:1.2.0'
implementation 'androidx.test.ext:junit:1.1.5'
implementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}
```
### Generate profile (Macrobenchmark)
```kotlin
// :baselineprofile/src/main/java/.../BaselineProfileGenerator.kt
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test
fun generate() = rule.collect(packageName = "com.acme.app") {
// 1. App 시작 — cold start
startActivityAndWait()
// 2. 핵심 user flow
device.findObject(By.res("login_button")).click()
device.wait(Until.hasObject(By.res("home_screen")), 5000)
// 3. Scroll
val list = device.findObject(By.res("feed_list"))
list.fling(Direction.DOWN)
list.fling(Direction.DOWN)
// 4. 다른 screen
device.findObject(By.text("Profile")).click()
device.wait(Until.hasObject(By.res("profile_screen")), 5000)
}
}
```
```bash
./gradlew :baselineprofile:generateBaselineProfile
# 결과: app/src/main/baseline-prof.txt
```
### Build 안 적용
```
- baseline-prof.txt 가 APK 안 포함.
- 첫 install 시 ProfileInstaller 가 dexopt.
- 사용자가 처음 launch 부터 빠름.
```
### Macrobenchmark — 측정
```kotlin
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test fun startupCold() = rule.measureRepeated(
packageName = "com.acme.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD,
) {
startActivityAndWait()
}
@Test fun scrollFeed() = rule.measureRepeated(
packageName = "com.acme.app",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
startupMode = StartupMode.WARM,
) {
startActivityAndWait()
val list = device.findObject(By.res("feed_list"))
list.fling(Direction.DOWN)
list.fling(Direction.DOWN)
}
}
```
```bash
./gradlew :baselineprofile:connectedAndroidTest
```
→ Result: startup time, frame timing, jank %.
### 기대 효과
```
Startup: 20-30% 빠름
Scroll: jank ↓
Compose 첫 frame ↓
```
→ R8 + Baseline Profile + Compose 가 함께 사용.
### Compose 최적화 (Baseline 와 별도 + 함께)
```kotlin
// 1. Stable / Immutable
@Stable data class UserState(...)
// 2. derivedStateOf
val isAtTop by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
// 3. key + contentType
items(items, key = { it.id }, contentType = { it.kind }) { ... }
// 4. animateItem
items(items, key = { it.id }) { it ->
Row(modifier = Modifier.animateItem()) { ... }
}
```
### Compose Compiler Metrics
```groovy
android {
composeOptions {
kotlinCompilerExtensionVersion = '1.5.10'
}
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs += [
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=$buildDir/compose_metrics",
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=$buildDir/compose_metrics",
]
}
}
```
→ Reports: 어떤 component 가 unstable / non-skippable.
### Startup 측정 in production
```kotlin
// Firebase Performance / 자체
class App : Application() {
override fun onCreate() {
val startupTrace = Firebase.performance.newTrace("app_startup")
startupTrace.start()
super.onCreate()
// ...
startupTrace.stop()
}
}
```
### App Startup library
```kotlin
class MyInitializer : Initializer<Unit> {
override fun create(context: Context) {
// 빠른 init
}
override fun dependencies() = emptyList<Class<out Initializer<*>>>()
}
```
```xml
<provider android:name="androidx.startup.InitializationProvider" ...>
<meta-data android:name=".MyInitializer" android:value="androidx.startup" />
</provider>
```
→ ContentProvider 보다 빠른 init.
### CI 안 generation
```yaml
# .github/workflows/baseline-profile.yml
- name: Build + generate
run: ./gradlew :baselineprofile:generateBaselineProfile
- name: Commit if changed
uses: stefanzweifel/git-auto-commit-action@v5
with:
file_pattern: 'app/src/main/baseline-prof.txt'
commit_message: 'chore: update baseline profile'
```
### Profile vs ART AOT
```
ART (5.0+): 자동 PGO — 사용 후 점진 빠름.
Baseline Profile: 첫 사용부터 빠름.
→ 둘 다.
```
### Common gotchas
```
- emulator 결과 ≠ 실기. 실기에서 측정.
- Cold start 만 측정 — process 재시작.
- Macrobenchmark 가 자체 process — system overhead.
- Profile 갱신 — 매 release 권장.
- Compose version 변경 시 다시 generate.
```
### Frame timing
```
StartupTimingMetric: Activity launch 시간
FrameTimingMetric: p50/p95/p99 frame time
TraceSectionMetric: 자체 trace section
PowerMetric: 전력
NetworkUsageMetric: bytes
```
### Custom trace
```kotlin
// App code
Trace.beginSection("loadHomeFeed")
val items = repo.loadFeed()
Trace.endSection()
// Macrobenchmark
metrics = listOf(TraceSectionMetric("loadHomeFeed"))
```
### Profile size
```
일반: 5-50 KB.
큰 app: 200 KB 까지.
APK 안 별 영향 X.
```
## 🤔 의사결정 기준
| 상황 | 적용 |
|---|---|
| 새 release | Baseline Profile 항상 |
| Compose UI 무거움 | 매우 효과 |
| 작은 utility app | 큰 효과 X |
| Startup 핵심 | 우선순위 |
| Scroll-heavy | 매우 효과 |
| Game | Profile 보다 game-specific |
## ❌ 안티패턴
- **Profile 한 번만 + 영원 사용**: 매 release 갱신.
- **Emulator 만 측정**: 실기와 다름.
- **모든 path profile**: 큰 file. critical 만.
- **R8 / minification 없이**: 효과 적음.
- **App Startup library 안 씀 + 큰 init**: cold start 느림.
- **Macrobenchmark 없는 측정**: 추측.
- **Compose Compiler Metrics 무시**: 어떤 게 unstable 모름.
## 🤖 LLM 활용 힌트
- Macrobenchmark 가 측정 + profile 생성.
- 매 release CI 갱신.
- Compose stable / immutable + key + animateItem.
- App Startup library 로 init 빠르게.
## 🔗 관련 문서
- [[Android_LazyList_Performance]]
- [[Android_Compose_Recomposition_Pitfalls]]
- [[Mobile_App_Size_Optimization]]