Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

[단축본] 컴포즈 내부로 이해하는 최적화 비법

[단축본] 컴포즈 내부로 이해하는 최적화 비법

Ji Sungbin

April 29, 2023
Tweet

More Decks by Ji Sungbin

Other Decks in Programming

Transcript

  1. @Composable fun UiNodes() { Column { repeat(3) { index ->

    Text(text = "My index is $index") } } } Column Text Text Text
  2. @Composable fun UiNodes() { Column { repeat(3) { index ->

    Text(text = "My index is $index") } } } Ui Node Ui Node Ui Node Ui Node
  3. /** * ஹನૉ੄ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೞח ௿ېझੑפ׮. * Gap

    Buffer ੗ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘ੉ఠח Array<Any?>ী ੷੢ؾפ׮. */ internal class SlotTable : CompositionData, Iterable<CompositionGroup>
  4. Gap Buffer: ࢎਊೡ ҕрٜਸ ޷ܻ ഛࠁ೧ فҊ, ೧׼ ҕрীࢲ ੘সਸ

    ૓೯ೞח ੗ܐ ҳઑ. ਤఃೖ٣ইীࢲח زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ ੉ۄҊ ੿੄ೞҊ ੓णפ׮.
  5. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն
  6. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ
  7. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ 3. 1ߣ૩ ੋؙझ੄ i ઁѢ [H, (v)_, _, _, _, _, _, _, _, _] // cursorܳ iо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ // ੉റ Gapীࢲ ч ઁѢо ૓೯غ޲۽ O(1)݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gapਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮)
  8. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ੤ cursorܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1)ী ৮ܐؽ 3. 1ߣ૩ ੋؙझ੄ i ઁѢ [H, (v)_, _, _, _, _, _, _, _, _] // cursorܳ iо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ // ੉റ Gapীࢲ ч ઁѢо ૓೯غ޲۽ O(1)݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gapਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮) 4. 0ߣ૩ ੋؙझ੄ H ܳ Bye ۽ ߸҃ [(v)B, y, e, _, _, _, _, _, _, _] // cursorܳ Hо ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N)੉ ѦܻҊ // ੉റ ݽف Gapীࢲ ч সؘ੉౟о ૓೯غ޲۽ O(1)݅ী ৮ܐؽ
  9. var isLoading = false @Composable fun Main() { Column {

    // 1ߣ૩ ਤ஖ (അ੤ cursor) Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap ߣ૩ਤ஖
  10. var isLoading = true @Composable fun Main() { Column {

    // 1ߣ૩ ਤ஖ (੉੹ cursor) Text(text = "Column Data") if (isLoading) { // 2ߣ૩ ਤ஖ (അ੤ cursor) Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Text “Loading…” ߣ૩ਤ஖ ߣ૩ਤ஖
  11. /** * Gapਸ ޖदೞҊ ౠ੿ ਤ஖੄ ੺؀੸ੋ ੋؙझܳ оܰఃח ېಌੑפ׮.

    (ےؒ ঘࣁझ ഝࢿച) * * ےؒ ঘࣁझ: ؘ੉ఠܳ ੷੢ೞח ࠶۾ਸ ೠߣী ৈ۞ ѐ ঘࣁझೞח Ѫ੉ ইפۄ * ౠ੿ ਤ஖۽ ߄۽ ੽Ӕೞৈ ೠ ߣী ೞա੄ ࠶۾ਸ ঘࣁझೞח ߑध */ internal class Anchor(loc: Int) { internal var location = loc val valid get() = location != Int.MIN_VALUE fun toIndexFor(slots: SlotTable) = slots.anchorIndex(this) fun toIndexFor(writer: SlotWriter) = writer.anchorIndex(this) }
  12. var isLoading = true @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { // Anchor۽ ߄۽ ੽Ӕ Text(text = "Loading...") } } } Column “Column Data” Gap Gap Gap Gap Text “Loading…” "ODIPS۽߄۽੽Ӕ Text
  13. @Composable fun Main() { Column { Text(text = "Column Data")

    } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap
  14. @Composable fun Main() { Column { Text(text = "Column Data")

    } } “Column Data” Gap Gap Gap Gap Gap Gap Group Group
  15. “Column Data” Gap Gap Gap Gap Gap Gap Group Group

    3FTUBSUBCMF(SPVQ 3FQMBDFBCMF(SPVQ .PWBCMF(SPVQ @Composable fun Main() { Column { Text(text = "Column Data") } }
  16. // RestartableGroup: ܻஹನ૑࣌੉ ੌযզ ࣻ ੓ח ஹನ੷࠶ // ઱ਤ۽ ࢤࢿػ׮.

    ߹ب੄ ২࣌੉ হ׮ݶ ݽٚ ஹನ੷࠶ী // ࢤࢿغח Ӓܛ੉Ҋ, ܻஹನ૑࣌ ೞח ߑߨਸ оܰ஘׮. // ݽٚ RestartableGroup਷ ೧׼ ߧਤ ݅ఀ // ੗୓੄ ܻஹನ૑࣌ झ௏೐(RecomposeScope)ܳ ഋࢿೠ׮. @Composable fun RestartableContainer() { Text(text = "SungbinLand") } RestartableGroup “SungbinLand” Gap Gap Gap Gap Gap Gap RestartableGroup 3FTUBSUBCMF$POUBJOFS  3FDPNQPTF4DPQF 5FYU  3FDPNQPTF4DPQF
  17. // ReplaceableGroup: ࠙ӝী ٮۄ ੤ߓ஖ ؼ ࣻ ੓ח // ஹನ੷࠶

    ઱ਤ۽ ࢤࢿػ׮. // Ӓܛਸ Ү୓೧ঠ ೡ ٸ ੘ࢿػ ؘ੉ఠܳ ੿ܻೞח ߑߨਸ оܰ஘׮. @Composable fun ReplaceableContainer(isColumn: Boolean = true) { when (isColumn) { true -> Text(text = "Column") else -> Text(text = "Row") } } RestartableGroup “Column” Gap Gap Gap Gap Gap ReplaceableGroup 3FQMBDFBCMF$POUBJOFS XIFO RestartableGroup 5FYU
  18. /** * ஹನ੷࠶੉ ߓ஖ػ ਤ஖о ׳ۄ૑ݶ ਤ஖ ݫݽ੉ઁ੉࣌੄ keyо ׳ۄઉࢲ

    ؘ੉ఠо ୡӝചؾפ׮. * ؘ੉ఠܳ ਬ૑ೠ ࢚క۽ ਤ஖ܳ ৤ӝӝ ਤ೧ࢶ key ஹನ੷࠶ਸ ੉ਊ೧ * ਤ஖ ݫݽ੉ઁ੉࣌ীࢲ ଵҊೡ keyܳ ૒੽ ੿੄೧ঠ ೤פ׮. * * key ஹನ੷࠶ਸ ੉ਊ೧ ߓ஖ػ ஹನ੷࠶਷ ઱ਤ۽ MovableGroup੉ ࢤࢿؾפ׮. * MovableGroup਷ ஹನ੷࠶੉ ਤ஖ ݫݽ੉ઁ੉࣌ key৬ ؘ੉ఠܳ ೦࢚ ࠁઓೞݴ * ੉زೞח ߑߨਸ оܰ஝פ׮. */ @Composable inline fun <T> key( vararg keys: Any?, block: @Composable () -> T ) @Composable fun MovableContainer() { key(Any()) { Text(text = "Key") } } RestartableGroup “Key” Gap Gap Gap Gap Gap MovableGroup .PWBCMF$POUBJOFS LFZ RestartableGroup 5FYU
  19. !3FBE0OMZ$PNQPTBCMF object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get()

    = LocalColors.current val typography: Typography @Composable @ReadOnlyComposable get() = LocalTypography.current // … } @Composable @ReadOnlyComposable fun stringResource(@StringRes id: Int): String { val resources = resources() return resources.getString(id) }
  20. var isLoading = false @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap Composition
  21. var isLoading = true @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Text “Loading…” Recomposition
  22. @Composable fun Main() { var number by remember { mutableStateOf(0)

    } Button(onClick = { number++ }) { Text(text = number.toString()) } } Button Function0<Unit> Gap Gap Gap Gap Text “$number” Smart-Recomposition Gap
  23. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* ܻஹನ૑࣌੉ য٣ী ૓೯ؼөਃ? 1, setContent content ৔৉ 2. Text 3. setContent content ৔৉ + Text */
  24. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* [first-composition] [re-composition] setContent recomposition setContent recomposition Text recomposition Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ */
  25. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } /* [first-composition] [re-composition] setContent recomposition Text recomposition Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ Button recomposition */
  26. EPOVUIPMFTLJQQJOH // ஹ౵ੌ ੹ @Composable fun TextWrapper(text: String) { Text(text

    = text) } // ஹ౵ੌ റ (೨ब ࠗ࠙݅ ಴द) fun TextWrapper(composer: Composer, text: String) { // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ૑࣌ झ௏೐ܳ ୶оೞҊ ੓਺ composer.startRestartGroup(-199242123) Text(text = text) composer.endRestartGroup() }
  27. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } setContent { var number by remember { mutableStateOf(0) } println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Text RecomposeScope 

  28. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Button RecomposeScope
  29. Composition Layout Draw @Composable fun Main() { Text( modifier =

    Modifier .clickable { getOne() }, text = "Hello, World!" ) } fun getOne() = 1
  30. @Composable fun Main() { Text( modifier = Modifier .clickable {

    getOne() }, text = "Hello, World!" ) } fun getOne() = 1 RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Gap Gap Gap Gap
  31. Modifier.offset { IntOffset(x = 0, y = 0) } Modifier.layout

    { measurable, constraints -> val placeable = measurable.measure(constraints) layout(width = 0, height = 0) { placeable.place(x = 0, y = 0, zIndex = 1f) } } Modifier.graphicsLayer { scaleX = 1f; rotationX = 0f; clip = false shape = RectangleShape; translationX = 0f alpha = 1f; shadowElevation = 0f } SomeGroup Function0<Unit> Gap Composition Layout Draw Function0<Unit> Gap Gap Gap Gap Function0<Unit>
  32. Modifier.offset { IntOffset(x = 0, y = 0) } Modifier.layout

    { measurable, constraints -> val placeable = measurable.measure(constraints) layout(width = 0, height = 0) { placeable.place(x = 0, y = 0, zIndex = 1f) } } Modifier.graphicsLayer { scaleX = 1f; rotationX = 0f; clip = false shape = RectangleShape; translationX = 0f alpha = 1f; shadowElevation = 0f } SomeGroup Function0<Unit> Gap Composition Layout Draw Function0<Unit> Gap Gap Gap Gap Function0<Unit> Skippable
  33. RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Text

    Int “Hello, World!” ۨ੉ইਓઁডઑѤ DPOTUSBJOUT ҅࢑ ठܶప੉࠶ч҅࢑ JOWPLF  6*౟ܻҳ୷
  34. @Composable fun Main() { Text( modifier = Modifier .clickable {

    getOne() }, text = "Hello, World!” ) } fun getOne() = 1 RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Gap Gap Gap Gap
  35. Composition Layout Draw Composition DPOTUSBJOUT ࠗݽ੄ઁডઑѤ֢୹ @Stable interface BoxWithConstraintsScope :

    BoxScope { val constraints: Constraints val minWidth: Dp val maxWidth: Dp val minHeight: Dp val maxHeight: Dp }
  36. Composition Layout Draw ן਷ୡӝച @Composable fun LazyLayout() @Composable fun LazyRow()

    @Composable fun LazyColumn() -B[Z-BZPVU਷ചݶীաఋաӝ߄۽੹ী-BZPVU%SBX૓೯
  37. 4OBQTIPU  SomeGroup 1 Snapshot Gap Gap Gap Gap Gap

    4OBQTIPU 2 Snapshot 4OBQTIPU 3 Snapshot
  38. /4OBQTIPU  SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2

    Snapshot 3 Snapshot 5 Snapshot N Snapshot 7 Snapshot
  39. SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2 Snapshot 3

    Snapshot 5 Snapshot N Snapshot 7 Snapshot 4OBQTIPU3FBE 4OBQTIPU8SJUF 
  40. SomeGroup px - snapshot px4 - snapshot px2 - snapshot

    px3 - snapshot @Composable fun Main() { val px = 1.dp.px val px2 = 2.dp.px val px3 = 3.dp.px val px4 = 4.dp.px } inline val Dp.px @Composable @ReadOnlyComposable get() = with(LocalDensity.current) { toPx() } Gap Gap Gap Gap
  41. SomeGroup density - snapshot @Composable fun Main() { val density

    = LocalDensity.current val px = with(density) { 1.dp.toPx() } val px2 = with(density) { 2.dp.toPx() } val px3 = with(density) { 3.dp.toPx() } val px4 = with(density) { 4.dp.toPx() } } Gap Gap Gap Gap Gap Gap Gap
  42. DPNQPTJUJPO-PDBM0G val LocalTextSelectionColors = compositionLocalOf { DefaultTextSelectionColors } val LocalTextStyle

    = compositionLocalOf { TextStyle.Default } val LocalConfiguration = compositionLocalOf<Configuration>( neverEqualPolicy() 
 ) { noLocalProvidedFor("LocalConfiguration") } val LocalContentAlpha = compositionLocalOf { 1f } SomeGroup CompositionLocal CompositionLocal Composable CompositionLocal CompositionLocal Composable Composable Composable
  43. TUBUJD$PNQPTJUJPO-PDBM0G val LocalContext = staticCompositionLocalOf<Context> { noLocalProvidedFor("LocalContext") } val LocalClipboardManager

    = staticCompositionLocalOf<ClipboardManager> { noLocalProvidedFor("LocalClipboardManager") } val LocalDensity = staticCompositionLocalOf<Density> { noLocalProvidedFor("LocalDensity") } val LocalFocusManager = staticCompositionLocalOf<FocusManager> { noLocalProvidedFor("LocalFocusManager") } StaticCompositionLocal Composable Composable Composable Composable Composable SomeGroup Composable Composable
  44. ↟ೣԋࠁݶજਸ੗ܐ 4. Wrap-up  -BNCEB0QUJNJ[JOH  -JWF-JUFSBM  $PNQJMFS.FUSJDT 

    4OBQTIPU4ZTUFNXJUI.7$$  #BTFMJOF1SPGJMFT  $PNQPTBCMF(SPVQT  4UBCJMJUZ4ZTUFN