[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
@@ -0,0 +1,358 @@
---
id: android-compose-performance
title: Compose Performance — recomposition / stability
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [android, compose, performance, vibe-coding]
tech_stack: { language: "Kotlin", applicable_to: ["Android"] }
applied_in: []
aliases: [Compose performance, recomposition, stability, immutable, baseline profile, strong skipping mode]
---
# Compose Performance
> Recomposition 폭발 = jank. **Stable / immutable, derivedStateOf, baseline profile, strong skipping mode**.
## 📖 핵심 개념
- Recomposition = render again.
- Stable = type 가 변경 시 비교 가능 → skip 가능.
- Strong skipping mode (Kotlin 2.0+) = 자동 skip.
- Baseline profile = startup 빠름.
## 💻 코드 패턴
### Recomposition trace
```kotlin
@Composable
fun MyComponent() {
SideEffect {
Log.d('Compose', 'recomposed')
}
// ...
}
```
→ Frequency 측정.
### Stability annotation
```kotlin
@Stable
data class User(
val id: String,
val name: String,
)
@Immutable
data class Config(...)
```
→ Compose 가 skip 가능.
### Unstable의 함정
```kotlin
// ❌ List 가 unstable
@Composable
fun ItemList(items: List<Item>) {
// 매 recomposition.
}
// ✅ ImmutableList (kotlinx.collections.immutable)
@Composable
fun ItemList(items: ImmutableList<Item>) {
// Skip 가능.
}
```
→ kotlinx.collections.immutable.
### Lambda stability
```kotlin
// ❌ 매 recomposition 가 새 lambda
@Composable
fun Screen(viewModel: ViewModel) {
Button(onClick = { viewModel.action() }) { ... }
}
// ✅ Method reference (stable)
@Composable
fun Screen(viewModel: ViewModel) {
Button(onClick = viewModel::action) { ... }
}
// ✅ remember
@Composable
fun Screen() {
val onClick = remember { { /* ... */ } }
Button(onClick = onClick) { ... }
}
```
### Strong Skipping Mode (Kotlin 2.0+)
```kotlin
// build.gradle.kts
kotlin {
compilerOptions {
freeCompilerArgs.add('-P plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true')
}
}
```
→ 자동 skip (lambda 도). Magic 식.
### derivedStateOf
```kotlin
@Composable
fun Screen(items: List<Item>) {
val hasMore by remember(items) {
derivedStateOf { items.size > 100 }
}
// hasMore 가 변경 시 만 update.
}
```
→ Computed value memoize.
### key (recomposition 정밀)
```kotlin
@Composable
fun ItemList(items: List<Item>) {
LazyColumn {
items(items, key = { it.id }) { item ->
ItemRow(item)
}
}
}
```
→ Item 의 identity. Reorder 시 잘못 recompose 안.
### LazyColumn / LazyRow
```kotlin
LazyColumn {
items(1000) { index ->
Card { Text('item $index') }
}
}
```
→ Visible 만 render. RecyclerView 의 modern.
### Layout inspector
```
Android Studio:
- Layout Inspector.
- Compose tree.
- Recomposition count per node.
```
→ "이 node 가 가짜 recompose?".
### Tracing
```kotlin
// In code
import androidx.compose.runtime.tooling.*
Trace.beginSection('MyComponent')
// ...
Trace.endSection()
```
→ Systrace 가 visible.
### Baseline profile
```kotlin
// macrobenchmark module
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test
fun generate() = rule.collect(packageName = 'com.example.app') {
startActivityAndWait()
// critical user flow
}
}
```
→ AOT compile of hot code. Cold start 30-50% 빠름.
### Stability inspect
```bash
./gradlew assembleRelease -PenableComposeCompilerReports=true
# Generated:
# build/compose_metrics/-classes.txt
# - Stable / unstable 매 class.
```
→ "왜 unstable?" 검증.
### List 의 함정
```kotlin
// ❌
data class State(val items: List<String>)
// → List 가 unstable.
// ✅
data class State(val items: ImmutableList<String>)
```
### Function 의 함정
```kotlin
// ❌ Function type 가 unstable 가 default
data class State(val onClick: () -> Unit)
// ✅
@Stable data class State(val onClick: () -> Unit)
```
→ Strong skipping mode 가 자동 fix.
### remember vs derivedStateOf
```kotlin
val sorted = remember(items) { items.sortedBy { it.name } }
// → items 가 변경 시 recalculate.
val hasMore by remember { derivedStateOf { items.size > 100 } }
// → items 가 변경 + hasMore 가 변경 시 만 update.
```
→ derivedStateOf 가 fine-grained.
### Immutable collection
```kotlin
import kotlinx.collections.immutable.*
val list: ImmutableList<Item> = persistentListOf(...)
val map: ImmutableMap<String, Int> = persistentMapOf(...)
```
→ Compose 가 stable 인식.
### Stop animation 시
```kotlin
val transition = rememberInfiniteTransition()
val rotation by transition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(...)
)
// Visible 안 = 자동 stop.
```
→ Battery 친화.
### Modifier order
```kotlin
// Order 가 important
Box(modifier = Modifier
.padding(8.dp)
.background(Color.Red)
.size(100.dp)
)
```
→ Modifier 가 chain. 매 modifier 가 cost.
### Profile
```kotlin
// Layout Inspector → Recomposition Count.
// 큰 number = bottleneck.
// Profile mode (release):
./gradlew assembleRelease
adb install ...
adb shell setprop debug.layout true
```
### CompositionLocal 의 함정
```kotlin
val LocalUser = compositionLocalOf<User> { error('not provided') }
// 매 user 변경 = 매 consumer recompose.
```
→ 자주 변경 = unstable.
### Jetpack Compose Compiler Metrics
```bash
# build/compose_metrics/
- *-classes.txt (stability)
- *-composables.txt (skippable, restartable)
```
→ Skippable: input 같음 = skip OK.
Restartable: trigger restart of recomposition.
### Performance tier
```
1. Layout Inspector (initial check).
2. Compose Compiler Reports (stability).
3. Tracing (Trace.beginSection).
4. Baseline profile (startup).
5. Macrobenchmark (real device).
```
### Real-world tips
```
- 큰 list = LazyColumn + key.
- ImmutableList default.
- Strong skipping mode (Kotlin 2.0+).
- Method reference > lambda.
- @Stable / @Immutable annotation.
- Profile 매 release.
```
### vs RecyclerView (옛)
```
RecyclerView: imperative, 빠름.
LazyColumn: declarative, 비슷 빠름.
→ 현재 = LazyColumn.
```
### Common 함정
```
- List 가 unstable.
- Lambda 가 매번 새.
- ViewModel state 가 큰 object.
- 매 recomposition 가 expensive work.
- LazyColumn 가 key 없음.
- Modifier 가 매번 새.
```
## 🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Custom data class | @Stable / @Immutable |
| List | ImmutableList |
| Lambda | Method reference / remember |
| Compute | derivedStateOf |
| Big list | LazyColumn + key |
| Cold start | Baseline profile |
| Profile | Layout Inspector + tracing |
## ❌ 안티패턴
- **List 가 unstable**: skip 안 됨.
- **Lambda 가 매번**: recompose 폭발.
- **No key in LazyColumn**: identity 깨짐.
- **CompositionLocal 자주 변경**: 폭발.
- **Big object state**: 매 변경 = 큰 recompose.
- **Profile 안 함**: blind.
## 🤖 LLM 활용 힌트
- Stable / immutable 가 핵심.
- Strong skipping mode (Kotlin 2.0+).
- Baseline profile 가 cold start.
- Layout Inspector 가 visible.
## 🔗 관련 문서
- [[Android_Compose_Recomposition_Pitfalls]]
- [[Android_Compose_State_Hoisting]]
- [[Android_Baseline_Profile]]