One of the biggest downsides of Compose for me is ...
# compose
a
One of the biggest downsides of Compose for me is the performance. Eventhough I have R8 enabled and I’m running in release build (which is also a downside cause it takes hours to build and heats up my laptop) it is still a poor experience. HorizontalPager + LazyVerticalGrid with 3 columns = 10 FPS Same setup on XML = extremely fast.
2
Super frustratet right now, I build this app over the last 8 months and its finally finished but the one part with the HorizontalPager + LazyVerticalGrid is giving my app such an bad quality feeling that I really can’t publish it like that
z
I struggled with some lingering performance issues after adopting compose. Pager + LazyList + LazyGrid in one screen was very challening to get smooth, but as with anything - if you throw enough shit at the wall, eventually something will stick. In my case, it wasnt so much so the components themselves, but rather the amount of recompositions I was causing - across all these components. After solving that, Ive found that performance can be incredible with compose - even better than what Ive acheived with views in the past. Id encourage you to read up on the performance documentation (https://developer.android.com/jetpack/compose/performance). Composable metrics can also give you an indicator about the inferred stability of classes in your project (https://chris.banes.dev/composable-metrics/) and whether or not your composable functions can be skipped (e.g. not recompose everytime something might have changed).
a
@Zoltan Demant That gives me some hope, thank you! Will take a look into it
z
🙏🏽 Good luck! And this slack is a superb resource too, Im not sure I wouldve been able to fix the issues I was facing without it!
a
Its super weird, I extracted the functionality with HorizontalPager + LazyverticalGrid into a new Project and it is running perfectly smooth. Could it be that the app size in general has an impact on the performance? I have a full Social Media app with 100+ Screens, Login and upload system, Sound editor and photo editor.
App Size is 100MB in release build
z
You could try including more of your code in the new project, e.g. navigation and stuff that wraps the pager + grid. Eventually (hopefully) you will notice that performance is horrible again and know what caused it. Another approach is using the profiler in Android Studio to check if CPU or memory usage is way high from work in the background. From my personal experience, firebase could eat 250 mb of memory - my app usually uses around 40 mb. It didnt cause massive lag, but definitely had an impact.
s
As a side note, you can try running
Copy code
adb shell cmd package compile -r bg-dexopt com.example.application
and see if you still experience poor performance. However, even after doing so, I still face a performance penalty if compared to XML - especially when quickly scrolling a
LazyColumn
.
z
@ste Are the models that you use in
item {}
stable/immutable? That made a huge difference for me, non stable meant 3 recompositions everytime the item became visible on screen; stable brought that down to 1. Usually its fine with 3 even, but if the items are complex it might take 5-10 ms to invoke the composable function, do that times 3 and you have lag.
s
@Zoltan Demant Unfortunately I can't make the entire composable tree stable because I need to display an image (`Painter` is unstable)
c
can’t you substitute the Painter with something stable? Eg the url or resource ID of the image
z
That, or include it in another class that you can mark
@Stable
🙂
Also, measure if it makes a difference. If youre just passing the painter to a few simple composables, it might not matter whether or not they recompose 1 or 3 times.
s
These three simple `Image`s have a huge performance malus...
z
That doesn't look right. Can you share the code for the screen?
s
Note1: things get noticeably better if I factor out the
painterResource
calls. E.g. From:
Copy code
LazyColumn { items { Image(painterResource(id)) } }
To:
Copy code
val painter = painterResource(id)
LazyColumn { items { Image(painter) } }
FYI the simple
Column
counterpart has no problems
z
@ste Would you say that performance is (near) perfect if you avoid calling painterResource for every item?
Youre basically performing all the work in painterResource for every item as you scroll, I havent used it myself but looking at the source code - it seems like quite a lot of work if you want things to be smooth! Another potential workaround (beyond declaring it outside of the list scope) would be to use vector icons (e.g. Icons.Rounded.Plus, etc).
s
The performance gets perfect only if I remove every
clickable
modifier from the `Image`s (second video). Otherwise, some jank still appears when the scroll is fast; but it's still a big improvement if compared to the starting scenario
z
Which compose version are you using? It was a pretty good while back, but at the time the clickable modifier had a pretty big impact on performance
I am using clickable and not seeing any issues 🤔 but on the latest beta release.
s
I'm using the latest beta, too
z
Which phone and android version are you testing on?
s
Damn, yours is very smooth. Yeah, R8 is enabled in full mode - I think it's a problem of either compose or my app :/
However, the problem is not related to that particular screen, but to every
LazyColumn
I use
z
I guess, in a way its a good thing that it affects every lazylist - because when you do find the solution it will likely fix the issue everywhere. But I know that it can be quite frustrating - as I mentioned above, Ive been there too and spent sooo many hours digging to find the underlying issue. Its very unlikely that this is the case, but you could also check whether anything outside of the
LazyColumn
is recomposed as youre scrolling. For example, in the RouteHandler, the targetState is a "complex" object, earlier I had an issue where
AnimatedContent
animations were being triggered a lot more (but you cant actually see it, since the end result is the same) than expected. At the end of the day that resulted in 3x more recompositions, for everything inside the content block. Ill let you know if anything else comes to mind, but for now thats all Ive got. Im sure you will work it out one way or another though! 💪🏽
What issues are you seeing @Tariyel Islami? Performance? It does look like youre also using painterResource for every item, which likely affects performance negatively!
t
@Zoltan Demant Lazycolumn gets stuck while scrolling. I need to pull from internet using AsyncImage in a real application. Will it give better results?
z
Its hard to tell, but you could experiment with moving the painterResource(xx) calls outside of the
item {}
block. Im honestly not sure how expensive (or if it is at all) the call is, but seemingly theres a bit of work there, that would happen a lot as youre scrolling the list.
c
Since we're talking about performance, is there an easy way to mimic release performance (with full R8) for debug builds?
z
The latest beta release seems to be very smooth, even in debug. It's not exactly like release, previously I would use a "fake" release build for the performance while debugging, but that's no longer needed.
s
@Zoltan Demant Out of scope, but, what's the app you showed in the video? I'd like to test it so I can determine whether it's a problem of mine.
z
These changes aren't published yet, maybe 1-2 weeks out. I can give you a poke then if you want?
s
Sure, thanks
z
@ste The update will roll out on the beta channel early next week, you can opt-in here 🙂 If you want you can also compare the current release with the beta one to find out how much of a difference Stable/Immutable (and some other changes) brings to the table in terms of performance benefits (in my case).
f
@ste Did you find out what can be done about the Clickable modifier slowing down lazy lists?
790 Views