--- 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 { override fun create(context: Context) { // 빠른 init } override fun dependencies() = emptyList>>() } ``` ```xml ``` → 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]]