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
690
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
470
できる!ComposeでCollapsingToolbar
tomoya0x00
0
1.1k
ComposeのMutableStateってどうやってLocal Unit Testすれば良いの??
tomoya0x00
0
1.2k
意外と簡単?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
AI駆動開発を通して感じた、 AI時代のデザイナーの役割変化
whisaiyo
3
2.1k
「エンジニア進化論」2028年の開発完全自動化、エンジニアはどう進化するか
cyberagentdevelopers
PRO
6
5.2k
Snowflakeと仲良くなる第一歩
coco_se
4
470
データサイエンスを価値につなげるプロジェクト設計 〜 DS一年目が現場で得た気づき 〜
ysd113
1
240
Disciplined Vibes: Scaling AI-Assisted Engineering
sheharyar
0
140
作って終わりにしない タイミーのセマンティックレイヤー育成の現在地
chanyou0311
4
2.4k
新しいUbuntu/GNOMEが使いたいからXからWaylandへ移行頑張ってるの巻 2026-06-20
nobutomurata
0
110
Agent Skills設計で柔軟性と硬さのバランスが難しい話
nassy20
0
130
Socrates × Looker 〜セマンティックレイヤーで進化するデータ分析エージェント〜
hanon52_
3
2.4k
FDE という解 ― 暗黙知と明示知をつなぐ、伴走型エンジニアリング ―
otanet
0
160
AIネイティブな開発のサプライチェーンリスク対策 〜激動の開発現場でリスクに立ち向かう〜【ZennFes】
cscengineer
PRO
2
130
中期計画、2回作ってみた ~業務委託と正社員、両方の視点から~
demaecan
1
820
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.5k
Prompt Engineering for Job Search
mfonobong
0
340
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
HDC tutorial
michielstock
2
710
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
250
Paper Plane (Part 1)
katiecoart
PRO
0
9k
For a Future-Friendly Web
brad_frost
183
10k
Being A Developer After 40
akosma
91
590k
Designing for Performance
lara
611
70k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
380
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