Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Strong Skipping Mode によってrecompositionはどう変わったのか
Search
mikan
September 25, 2024
Technology
420
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Strong Skipping Mode によってrecompositionはどう変わったのか
DroidKaigi.onCompletion { 2024@Online }
https://yumemi.connpass.com/event/329691/
mikan
September 25, 2024
More Decks by mikan
See All by mikan
形式手法を使って仕様をコーディングしよう
mikanichinose
0
140
Navigation3でやりとりした画面引数をSavedStateHandleから引き出せない理由
mikanichinose
0
35
Lazy APIを使ってGradleビルド速度を改善する
mikanichinose
1
92
Navigation3でViewModelにデータを渡す方法
mikanichinose
0
770
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
240
RepositoryのSSoT化
mikanichinose
0
100
Kotlin Multiplatform 始めました
mikanichinose
1
170
Web APIをなぜつくるのか
mikanichinose
0
4.3k
イベントをどう管理するか
mikanichinose
3
420
Other Decks in Technology
See All in Technology
音声コミュニティを守るAI監視基盤_ 90%以上の入力削減を支えたServerless設計と運用判断
shuheioka123
0
120
GitHub Agentic Workflows を触ってみる
htkym
2
870
【データ横丁主催】AI Agentがコンテキストを使って仕事をした後、何が残るのか― 組織の経験を次の判断に引き継ぐ「Agent Memory」
shisyu_gaku
2
250
営業オントロジーの作り方と、エージェントからの辿り方 ── ナレッジワークの現場から
kworkdev
PRO
1
200
Lambda MicroVMsが分からなすぎたので使い所を1から考えてみた
tsukuboshi
1
240
10年欲しかった音楽管理アプリを、AIと一緒に作りはじめた
judau
1
190
カンファレンスに参加した後の浮遊感とセルフケア
pauli
0
210
雪かき部 #7 もう怖くない!SELECT文!
foursue
0
180
20260929_AmazonGuardDutyの検出通知メールにAWS DevOpsAgentの調査結果を追加する
yhana
1
360
AIは推し活である。
kurazuuuuuu
2
1k
Argo CDとAtlantisで実現するインフラ管理のセルフサービス化──小規模SREチームで支えるプラットフォーム
cassius7
0
250
人にやさしく、AIにやさしく、書き手を選ばないIaCのガードレール再考 / Rethinking IaC Guardrails for Humans and AI Alike
kohbis
5
1.8k
Featured
See All Featured
Ruling the World: When Life Gets Gamed
codingconduct
0
370
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Balancing Empowerment & Direction
lara
6
1.3k
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
Exploring anti-patterns in Rails
aemeredith
4
520
Mobile First: as difficult as doing things right
swwweet
225
10k
GitHub's CSS Performance
jonrohan
1033
470k
Transcript
Strong Skipping Mode によって recompositionはどう変わったの か DroidKaigi.onCompletion { 2024@Online }
mikan(一瀬喜弘)
自己紹介
目的 Strong Skipping Mode を有効にすることで、不安定なパラメ ータに依存するComposable関数のrecompositionに、どのよ うな違いが生じるのか検証してみた 注意 コード例は検証用のものなので、およそプロダクションコー ドで利用するようなものにはなってません
Strong Skipping Mode とは TL;DR 不安定なパラメータに依存しているComposable関数も skippableがつくようになった
Strong Skipping Mode を有効にする 方法はいろいろある 1. compose-runtime:1.7.0 2. kotlin 2.0.20
3. compose compiler にオプションを渡す // build.gradle.kts // 1. tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() { compilerOptions.freeCompilerArgs.addAll( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true", ) } composeCompiler { // 2. before kotlin 2.0.20 enableStrongSkippingMode = true // 3. after kotlin 2.0.20 featureFlags = setOf( ComposeFeatureFlag.StrongSkipping.disabled() // 無効にする書き方
検証用コード @Composable fun Names( names: List<String>, ) { Row( horizontalArrangement
= Arrangement.spacedBy(8.dp), ) { names.forEach { Text(it) } } }
検証用コード class UnstableUserClass( val names: List<String>, ) @Composable fun User(user:
UnstableUserClass) { Names(user.names) }
検証用コード data class UnstableUserDataClass( val names: List<String>, ) @Composable fun
User(user: UnstableUserDataClass) { Names(user.names) }
検証用コード @Stable class StableUserClass( val names: List<String>, ) @Composable fun
User(user: StableUserClass) { Names(user.names) }
検証用コード @Immutable class ImmutableUserClass( val names: List<String>, ) @Composable fun
User(user: ImmutableUserClass) { Names(user.names) }
検証用コード @Stable data class StableUserDataClass( val names: List<String>, ) @Composable
fun User(user: StableUserDataClass) { Names(user.names) }
検証用コード @Immutable data class ImmutableUserDataClass( val names: List<String>, ) @Composable
fun User(user: ImmutableUserDataClass) { Names(user.names) }
@Composable fun Users( unstableUserClass: UnstableUserClass, unstableUserDataClass: UnstableUserDataClass, stableUserClass: StableUserClass, immutableUserClass:
ImmutableUserClass, stableUserDataClass: StableUserDataClass, immutableUserDataClass: ImmutableUserDataClass, names: List<String>, modifier: Modifier = Modifier, ) { Column(modifier) { User(unstableUserClass) User(unstableUserDataClass) User(stableUserClass) User(immutableUserClass) User(stableUserDataClass) User(immutableUserDataClass) Names(names) } }
@Composable fun MainScreen1( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = mutableListOf("mikan") val unstableUserClass = UnstableUserClass(names) val unstableUserDataClass = UnstableUserDataClass(names) val stableUserClass = StableUserClass(names) val immutableUserClass = ImmutableUserClass(names) val stableUserDataClass = StableUserDataClass(names) val immutableUserDataClass = ImmutableUserDataClass(names) Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
@Composable fun MainScreen2( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // positional memoized by remember val unstableUserClass = UnstableUserClass(names) val unstableUserDataClass = UnstableUserDataClass(names) val stableUserClass = StableUserClass(names) val immutableUserClass = ImmutableUserClass(names) val stableUserDataClass = StableUserDataClass(names) val immutableUserDataClass = ImmutableUserDataClass(names) Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
@Composable fun MainScreen3( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } val unstableUserClass = remember { UnstableUserClass(names) } val unstableUserDataClass = remember { UnstableUserDataClass(names) } val stableUserClass = remember { StableUserClass(names) } val immutableUserClass = remember { ImmutableUserClass(names) } val stableUserDataClass = remember { StableUserDataClass(names) } val immutableUserDataClass = remember { ImmutableUserDataClass(names) } Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
MainScreen1 すべてrecompositionした。recompositionの前後で見た目は変わらず → recompositionの度にすべての変数が再割り当てされているから → StableUserDataClassとImmutableUserDataClassがrecompositionした理由について、この説明だと違和感 が残る
MainScreen2 (mutableListにrememberをつけたやつ) @Stable と @Immutable を つけた data class だけスキップした。
recompositionした ものは 描画が 更新された
MainScreen2 (mutableListにrememberをつけたやつ) @Composable fun MainScreen2( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュが存在するので再割り当ては発生しない val unstableUserClass = UnstableUserClass(names) // UnstableUserClass(["mikan", "mikan"]) → UnstableUserClass(["mikan", "mikan"]) val unstableUserDataClass = UnstableUserDataClass(names) // UnstableUserDataClass(["mikan", "mikan"]) → UnstableUserDataClass(["mikan", "mikan"]) val stableUserClass = StableUserClass(names) // StableUserClass(["mikan", "mikan"]) → StableUserClass(["mikan", "mikan"]) val immutableUserClass = ImmutableUserClass(names) // ImmutableUserClass(["mikan", "mikan"]) → ImmutableUserClass(["mikan", "mikan"]) val stableUserDataClass = StableUserDataClass(names) // StableUserDataClass(["mikan", "mikan"]) → StableUserDataClass(["mikan", "mikan"])
MainScreen2 (mutableListにrememberをつけたやつ) @Composable fun MainScreen2( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュが存在するので再割り当ては発生しない val unstableUserClass = UnstableUserClass(names) // skippableでないのでrecompositionする val unstableUserDataClass = UnstableUserDataClass(names) // skippableでないのでrecompositionする val stableUserClass = StableUserClass(names) // skippableだが、再割り当てによって参照が変わっているのでrecompositionする val immutableUserClass = ImmutableUserClass(names) // skippableだが、再割り当てによって参照が変わっているのでrecompositionする val stableUserDataClass = StableUserDataClass(names) // skippableであり、equals比較において同じとみなされるのでrecompositionしない
MainScreen3 (変数すべてにrememberをつけたやつ) @Stable と @Immutable をつけたものについてはスキップした。recompositionしたものは描画が更新された
MainScreen3 (変数すべてにrememberをつけたやつ) @Composable fun MainScreen3( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserClass = remember { UnstableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserDataClass = remember { UnstableUserDataClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val stableUserClass = remember { StableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val immutableUserClass = remember { ImmutableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val stableUserDataClass = remember { StableUserDataClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない
MainScreen3 (変数すべてにrememberをつけたやつ) @Composable fun MainScreen3( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserClass = remember { UnstableUserClass(names) } // skippableでないのでrecompositionする val unstableUserDataClass = remember { UnstableUserDataClass(names) } // skippableでないのでrecompositionする val stableUserClass = remember { StableUserClass(names) } // skippableであり、再割り当ては発生していないのでスキップ val immutableUserClass = remember { ImmutableUserClass(names) } // skippableであり、再割り当ては発生していないのでスキップ val stableUserDataClass = remember { StableUserDataClass(names) } // skippableであり、再割り当ては発生していないのでスキップ
Strong Skipping Mode MainScreen1 すべてrecompositionした: 変化なし
Strong Skipping Mode MainScreen2 (mutableListにrememberをつけたやつ) @Stable と @Immutable をつけた data
class および listを単純に渡しているものについてはスキップした → listはキャッシュが使われるのでスキップしたと考えられる
Strong Skipping Mode MainScreen3 (変数すべてにrememberをつけたやつ) すべてスキップした → すべてskippableになり、再割り当ては発生していないのでスキップしたものと考えられる
Strong Skipping Mode 気になった点 @Composable fun Names( names: List<String>, )
{ Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { names.forEach { Text(it) } } } Names(listOf("mikan")) // skip
まとめ Strong Skipping Mode を有効化することで、不安定な型であっても同値であればrecompositionをスキップ するようになった 公式ドキュメントには、不安定な型については === で比較するとあったが、 ==
で比較しているように 見える これまでたまたま描画が更新できていた箇所が、Strong Skipping Mode を有効化によって更新しなくなる 可能性がある
ご清聴ありがとうございました