---
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]]
