Hey, It’s me again, the guy who just can’t get his...
# compose
a
Hey, It’s me again, the guy who just can’t get his HorizontalPager + LazyVerticalGrid to run smooth… After trying now for several months, again and again, to get the performance right I’m finally at a point where I lost hope. I read/watched everything I could find on Google and YouTube about Jetpack Compose’s performance and tried so many different things. Here is a short breakdown of the things I tried/checked: • No calculations etc in the composable • using remember {} when possible • only using stable data classes • Using @Immutable wrapper classes for my lists • Checked with compiler metric that all composables are skippable • Checked with Layout Inspector for recompositions (No unwanted re-compositions when swiping) • Generated Baseline Profiles for that swipe gesture • Enabled R8 + release build + shrinkResources • Uploaded to Google Play to download it from there so baseline profiles are installed (using Android 12 so no sideloading) • Using a key for my lists • Used Perfetto stack tracing to analyze what’s happening but besides janky frames can’t see anything wrong. (I’m gonna setup a big StackOverflow question the coming days where I explain everything I tried so far in detail) The exact same setup is running totally fine on XML with ViewPager + RecyclerView. The Items inside the LazyVerticalGrid consists of: • 2x TextViews • 1x AsyncImage loaded from URL (512x512) • 2x Icons Now I think all that’s left for me is to switch back to ViewPager + RecyclerView and try out interoperability or does anyone know why HorizontalPager + LazyVerticalGrid performs so poorly and how to fix it? Also is this a known issue and are there maybe some plans for the future to improve the performance?
m
Is it only this exact combination of features which are not smooth for you or do you have problems with, e.g., LazyColumns too?
a
No I only have problems with this combination. Also LazyVerticalGrid is running smooth when scrolling. But swiping the HorizontalPager is causing janky frames
r
I wonder if it's not a performance issue but a scheduling problem. Would you mind filing a bug and include a test project and/or the perfetto traces please?
@Leland Richardson [G] @Adam Powell Would you mind forwarding this to someone on the team and file an internal bug? I’m out for the next week
n
Could there also be a public ticket so we can track it? 😬 I've been having performance issues with nested lazy components too.
s
I think these perf issues are something we are already aware of. It is related to the fact that Lazy containers don't reuse other Lazy composables inside, so it forces complete recreation. We plan to address it, eventually, but at the current moment of time a workaround could be to use a non-lazy Row in place of viewpager if you have fixed small number of items. cc: @Andrey Kulikov, I believe it was tracked somewhere?
a
@shikasd could you please provide a little more details on the workaround? What exactly is a non-lazy Row and did you mean instead of a HorizontalPager? Or instead of the ScrollableTabRow?
b
This is definitely an issue on our side not yours, Aaron. It seems there is no easy performant way to achieve the layout you are trying to do right now. Andrei means instead of HorizontalPager, use a Row. It's not a simple workaround because a Row doesn't have snapping scroll so you would have to handle that. It might be possible to do slightly easier with the snapping that is in Compose 1.3, but I believe that currently is only for lazy lists.
@natario1 if you have a sample project, please file an issue!
l
Sorry I had a bit of a busy weekend, catching up now. One way to think about it is that LazyRow has some overhead over Row, but pays off when there is a lot of content and a lot of it is going to be off screen. The Lazy* APIs don’t reuse views to the extent that RecyclerView allows, so the way Lazy is implemented currently, if you have a Lazy nested inside of another Lazy, we end up paying that overhead every time. Ideally we would come up with some solution where the nested Lazy gets safely reused and the overhead is amortized with the parent Lazy. This is effectively what is happening with RecyclerView. With RecyclerView you manually express how to rebind an item, and because of that, you can just update the item’s RecyclerView instead of recreate it.
b
We have created a public issue for this as well, https://issuetracker.google.com/252846775
a
@Ben Trengrove [G] I see it is marked as fixed. So its worth spending the time and removing my workaround? Should the performance of the LazyVerticalGrid inside the HorizontalPager be smooth now?
b
Marked as fixed but not released yet. @shikasd will horizontal pager automatically use this or will we need updates there as well?
s
Hey, it brought certain improvements, but we had some issues with it, it was reversed for now Overall, for the example in this thread it wasn't that impactful, as most of the cost came from recreating clickable, which we don't reuse
I think it makes sense to reopen the ticket, I'll do it when I am next to the laptop :)
b
Reopened 👍 Thanks Andrei