Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Compose の LazyColumn パフォーマンス改善で取り組んだこと
Search
Tomoya Miwa
June 14, 2022
Technology
2.5k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Compose の LazyColumn パフォーマンス改善で取り組んだこと
sample source:
https://github.com/tomoya0x00/ComposeLazyColumnSandbox
Tomoya Miwa
June 14, 2022
More Decks by Tomoya Miwa
See All by Tomoya Miwa
基礎から学ぶ大画面対応(Learning Large-Screen Support from the Ground Up)
tomoya0x00
0
8.2k
Re:VIEWで書いた「Compose で Android の edge-to-edge に対応する」をRoo Codeで発表資料にしてもらった
tomoya0x00
0
710
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
480
できる!ComposeでCollapsingToolbar
tomoya0x00
0
1.1k
ComposeのMutableStateってどうやってLocal Unit Testすれば良いの??
tomoya0x00
0
1.3k
意外と簡単?Navigation rail導入のお話
tomoya0x00
0
1.6k
Kotlin Coroutines Flow を触ってみた話し
tomoya0x00
2
900
Android for Carsのお話し
tomoya0x00
1
1.1k
熟成されたアプリのmulti module化(halfway)
tomoya0x00
2
1k
Other Decks in Technology
See All in Technology
20260724 情シスAI #1 「全従業員をAIネイティブにする」ために情シスがやっていること(公開版)
frtckty
0
150
DevOps Agentで運用判断をチーム資産にする~Agent InstructionsとAgent Skillを継続的に育てる~
fujioka6789
0
180
NetBoxを利用した作業効率化の試み_NetDevNight4
tnoha
0
420
Atlassian Cloudサポート業務でのAIエージェント活用事例
smt7174
0
220
20260801_スクフェス大阪
kgnkhkr
0
330
plamo-3-translateの開発
pfn
PRO
0
240
QAタスクをスキル化したいときに考えること
aomoriringo
0
140
Power Automateアップデート情報
miyakemito
0
290
システム監視を 「システムを監視するだけ」で 終わらせないために
seiud
0
160
AIツールを導入しても生産性はあがらない? カオナビが直面した 3つの壁と乗り越え方。/ Overcoming 3 Barriers to AI-Driven Productivity at kaonavi
kaonavi
0
1.3k
SnowflakeCoCoでデータエンジニアリング!
foursue
0
110
データと地図で読む 大井町の「かわるもの、かわらないもの」
yoshiyama_hana
0
900
Featured
See All Featured
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
420
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Leo the Paperboy
mayatellez
8
2k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.4k
Why Our Code Smells
bkeepers
PRO
340
58k
A designer walks into a library…
pauljervisheath
211
24k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
920
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
370
Believing is Seeing
oripsolob
1
180
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
200
Transcript
Compose の LazyColumn パフォーマンス改善で取り組 んだこと 2022/06/14 Engineer LT#1 Android tomoya0x00
1
About me tomoya0x00 Twitter, GitHub Android U-NEXT Co., Ltd. 2
今回のLTが(もしかしたら)役に立つかもしれない? LazyColumnって、RecyclerViewよりもパフォーマンス良いんだよね? Composeのパフォーマンス問題って、Releaseビルドにしたら解決するんでしょ? 最新技術のComposeなんだから、従来のAndroid Viewよりもパフォーマンスで悩むこと は少ないはず! 3
お話しすること LazyColumnを気軽に使ったらパフォーマンス問題に直面し、 なんとかNexus 7 2013という低スペック端末でもある程度使えるレベルにした話。 4
目次 パフォーマンスに絶望した話 パフォーマンス問題に対する考察 対策その1: Glideを使ってみる 対策その2: さらにインスタンス生成を減らす 対策その3: 描画を遅延させてスキップ可能とする これまでのような対策の前に確認しておくと良い事
他にも効果がありそうな事 まとめと感想 5
パフォーマンスに絶望した話 6
まずは動画をご覧下さい 7
8
パフォーマンスに絶望した話 Nexus 7 2013で動かしました アニメーションGIFのフレームレートは30fpsです アニメーションGIFだからカクカクしているわけではありません Releaseビルドです もちろん、minifyEnabled true LazyRow
in LazyColumnで、各itemではCard内でCoilにより画像表示しているだけのシ ンプルなアプリです ちなみに、RecyclerViewだとさくさく動きます ソースコードのリンク: MainContentA 9
このままだとCompose化の夢が絶たれてしまう・・・ 10
パフォーマンス問題に対する考察 11
logcat GCのログが多いような気がする Background partial concurrent mark sweep GC freed 12609(781KB)
AllocSpace objects, 9(180KB) LOS objects, 39% free, 21MB/35MB, paused 5.401ms total 73.333ms Background partial concurrent mark sweep GC freed 9126(463KB) AllocSpace objects, 3(156KB) LOS objects, 33% free, 31MB/47MB, paused 7.476ms total 48.278ms Background partial concurrent mark sweep GC freed 122448(9MB) AllocSpace objects, 0(0B) LOS objects, 33% free, 24MB/36MB, paused 3.021ms total 171.936ms Background partial concurrent mark sweep GC freed 34312(1960KB) AllocSpace objects, 1(20KB) LOS objects, 33% free, 23MB/35MB, paused 1.861ms total 110.321ms Background partial concurrent mark sweep GC freed 302696(15MB) AllocSpace objects, 5(124KB) LOS objects, 36% free, 28MB/44MB, paused 2.227ms total 140.014ms Background partial concurrent mark sweep GC freed 95715(7MB) AllocSpace objects, 4(80KB) LOS objects, 33% free, 24MB/36MB, paused 1.861ms total 106.079ms ... 12
パフォーマンス問題に対する考察 Nexus 7 2013という(今となっては)低スペック端末にとっては、 インスタンス生成が走りすぎているのかも知れない。 CoilのAsyncImageは色々インスタンス生成している 各AsyncImageごとにcoroutineを起動しているっぽい? その他、色々インスタンス生成している Modifierもメソッドチェーンする度に新しいインスタンスを生成しているっぽい 確かにAsyncImage無しだとかなりパフォーマンスが改善したので、
まずはCoil使うのをやめてみる。 ※ 一般的なケースでは、Coilがボトルネックになることは無いとおもいます!! 13
対策その1: Glideを使ってみる 14
対策その1: Glideを使ってみる Glide版の画像読み込みComposableをつくってみた。 以前はaccompanistでComposable版が提供されていた実績がある 昔からあるライブラリだから、使用するリソース的にも優しいはず 確かにパフォーマンスは改善したけど、まだカクついてる。 ソースコードのリンク: MainContentBSimpleAsyncImage 15
対策その2: さらにインスタンス生成を減らす 徹底的にインスタンス生成を減らす。 MaterialのCard使うのをやめる 内部で色々インスタンス生成しているため 一度つくったModifierはキャッシュする ソースコードのリンク: MainContentC 16
Modifierのキャッシュ class ModifierCacheHolder { private val map = mutableMapOf<String, Modifier>()
@SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory") @Composable fun getOrCreate( tag: String, creator: @Composable () -> Modifier, ): Modifier = map[tag] ?: creator.invoke().also { map[tag] = it } } ソースコードのリンク: ModifierCacheHolder 17
Modifierのキャッシュ val modifierCacheHolder = remember { ModifierCacheHolder() } Column( modifier
= modifierCacheHolder.getOrCreate(tag = "MainRowRoot") { Modifier.padding(top = 8.dp) }, ) { ... } 18
Modifierのキャッシュ 基本的にModifierのインスタンスはStableなので、使い回しても大丈夫・・・なはず ただし、あんまり自信は無し 今のところ目に見えて不具合は起きていないので、たぶん大丈夫? ただし、Modifier.clickable()のインスタンスはキャッシュしちゃダメ 最初にセットしたクリックリスナーが使い回されてしまう clickable()の手前までのModifierのインスタンスなら、キャッシュしてOK 19
改善の成果をご覧下さい 20
21
残った課題に対する考察 22
残った課題に対する考察 高速なスクロールは、とてもカクつく 各行の描画にかかる時間が、そもそも1フレームを超えていそう そもそも高速スクロールで一瞬しか表示されないなら、描画をサボって良いのでは? 描画自体を遅延させてスキップ可能にしてみてはどうだろう? ソースコードのリンク: MainContentD 23
対策その3: 描画を遅延させてスキップ可能とする 24
対策その3: 描画を遅延させてスキップ可能とする @Composable fun LazyBox( modifier: Modifier = Modifier, delayMilliSec:
Long, content: @Composable BoxScope.() -> Unit, ) { Box( modifier = modifier, ) { var showContent by remember { mutableStateOf(false) } LaunchedEffect(Unit) { withContext(Dispatchers.Default) { delay(delayMilliSec) showContent = true } } if (showContent) content.invoke(this) } } ソースコードのリンク: LazyBox 25
対策その3: 描画を遅延させてスキップ可能とする LazyBox( delayMilliSec = 10, placeHolder = { Spacer(
modifier = modifierCacheHolder.getOrCreate(tag = "MainRowPlaceHolder") { Modifier.size(8.dp * 2 + 120.dp * 9f / 16) }, ) }, ) { LazyRow { items( items = data.rowIds, key = { it }, ) { rowId -> MainItemD( modifierCacheHolder = modifierCacheHolder, text = "${data.columnId}_$rowId", ) } } } 26
最終的にどうなったか 27
28
これまでのような対策の前に確認しておくと良い事 29
これまでのような対策の前に確認しておくと良い事 Compose Compiler Metricsのチェック https://github.com/androidx/androidx/blob/androidx- main/compose/compiler/design/compiler-metrics.md 特にListはそのままだとStable扱いにならないので要注意です 対策の例としては、@Immutableなdata classで包む Jetpack
ComposeのComposable関数の引数に別のモジュールのクラスを使うときの注 意点 https://qiita.com/takahirom/items/6907e810d3661e19cfcf 30
他にも効果がありそうな事 31
他にも効果がありそうな事 Compose 1.2のcontentType LazyListでRecyclerViewのようにComposableをもっと効率的に再利用するための もの ただし、今回の例に適用してもあまり効果が感じられなかった 使い方が悪い?ちゃんと計測したら、実は改善されているのかも? Baseline Profiles あらかじめよく使うパスを計測しておくことで、それらのバイトコードからコンパ
イルしてもらう、というもの? 効果が期待できそうだけど、まだ試せておらず 32
まとめと感想 33
まとめと感想 正直、やり過ぎたと思ってます たぶん、通常のアプリだとどうしても必要な場合だけLazyBoxいれる、ぐらいで良 いと思う そもそもBaseline Profilesで事足りるかも知れない 今時、Nexus 7 2013は流石に考慮しなくて良いと思う ただし、特にお手頃価格のタブレットはパフォーマンスが控えめな事もあるので、
実機で動作確認してみた方が良い 34