Hi Everyone, I had some observations and questions...
# compose
r
Hi Everyone, I had some observations and questions and I'm looking forward to get there answers. Below are the screenshots of a mobile device with GPU overdraw setting enabled, first is a RecyclerView and second a lazy Column. I'm feeding both RecyclerView and LazyColumn a Hardcoded list of items (same conditions for both). RecyclerView is using DiffUtil and LazyColumn is using the Key Composable and no recompositions are happening when I refresh (feed the same data again) The Lazy Column has purple and Green all over it depicting it has greater overdraw then RecyclerView which has purple only on it's View Holders depicting almost no overdraw. Greater overdraw means app is doing more rendering work than necessary. I'm using a release build apk for making observations. Q. Why the Lazy Column has this issue? Q. How can I convince my team to use Lazy Column or Compose given that it will be a critical part of the app but is has performance issues. I'm using Compose bom 2023.08.00 and have also attached the apk for observing LazyColumn Links: Tweet: https://twitter.com/avidRaghav/status/1693496857899454580?s=20 GPU Overdraw setting reference https://developer.android.com/topic/performance/rendering/inspect-gpu-rendering#debug_overdraw My LazyColumn Activity: https://github.com/avidraghav/ComposeVsViews/blob/main/app/src/main/java/com/raghav/composevsviews/LazyColumnActivity.kt My RecyclerView Activity: https://github.com/avidraghav/ComposeVsViews/blob/main/app/src/main/java/com/raghav/composevsviews/MainActivity.kt Thanks.
🧵 3
LazyColumn
RecyclerView
x
I see two main things that doesn’t align with Compose best practices there: 1. Don’t use the
State<T>
wrapper in the
ViewModel
. The
ViewModel
should only contain data that will then be turned into a Compose
State
by using
StateFlow::collectAsState
or
StateFlow::collectAsStateWithLifecycle
2. As the
List
Kotlin type is not immutable, but a read-only collection, it’s not treated as stable. You should be using a
PersistentList
or
ImmutableList
r
I used the Immutable List extension function and the results are same. Will make a commit now. But how can using State<T> affect the overdrawing issue?
x
I wouldn’t expect my first point there to be related with performance issues. It could, however, have other consequences like not taking into consideration the UI lifecycle
r
I have checked there are no unnecessary recompositions
x
I’ll give a sight to your next commit changing to immutable lists
You should be having unnecessary recompositions while using
List
as part of the state. It won’t be treated as stable
r
the apk I provided had the Immutable lists implemented, I somehow reverted the code before pushing. I have pushed changes again.
app-release-lazycolumn.apk
x
I’ve seen the change and that’s not what I was referring to, you’re still passing a
ListState.Content
as an argument to a Composable function. As this
ListState.Content
has a
List
property, it will be treated as unstable by the compiler. In the picture, the change that will make your state stable for Compose
r
I see I'll make the change and test.
👍 1
x
I recommend you to take a look at

this video from Google IO

where they show you this thing of
List
not being stable with an example and the fix
Make sure to keep testing on release mode with R8 enabled. You could also use the Jetpack Macrobenchmark library to test this performance-related issues and see the improvements/regressions with reliable measurements
r
The issue still persists, just pushed the changes and tested it
x
I recommend you to make a benchmarking test. If there are still performance-related issues, you could fill a bug in the Google Issue tracker, as they may have to delve into your case
r
thanks but I don't think benchmarking will help cause I'm not interacting with the app. The Screenshots are from the state when the first app is first opened.
I have filed bugs previously but they take a lot of time to resolve 😅
x
The Macrobenchmark library allows you to perform measurements on app startup too. You could also record a systrace and analyse it in the Perfetto UI tool. In the video above from this year’s Google IO, they show you how to do that
👍 1
Also, I’d like to know if @Leland Richardson [G], @George Mount, @Chuck Jazdzewski [G] (whom I’ve seen from ADB #199 that are working on Compose performance) or @romainguy (whose mind is full of knowledge of every possible topic 😂) are able to highlight something here that I may be missing
👍 1
a
Neither
SwipeRefreshLayout
nor
RecylerView
have their own background in your code, so they don't redraw over the already-existing window background (from
Theme.AppCompat
). On the other hand,
LazyColumnActivity
is supplied the same
AppCompat
theme, which means window background has already been drawn, but then you're also putting Compose's own theming on top of it. The whole point of Compose is to move away from traditional concepts and resource bloat. You should move away from
AppCompat
themes anyway, but if you can't, just use this override and you'll no longer see the additional overdraw:
Copy code
<item name="android:windowBackground">@android:color/transparent</item>
In any case, overdraw is one of the last things you need to optimize. In many cases it is unavoidable because of how Android draws its views: back-to-front. Everything counts as an overdraw if it has its own "view" (background, color, text, etc). First, focus on making sure your composables use
stable
parameters wherever possible, and that they're
restartable skippable
. See https://github.com/androidx/androidx/blob/androidx-main/compose/compiler/design/compiler-metrics.md for more info.
r
Couple of things about overdraw:
• What you are seeing is probably due to a
Surface
somewhere (in your
Scaffold
?)
• The rendering pipeline has been able to optimize away hidden surfaces like this for years now. The overdraw tool will disable the optimization so you can still see potential unnecessary layers in your UI but you won’t pay the cost for it
👍 2
• Overdraw is cheap (“free”) on tiling GPUs (all mobile GPUs today)
• Do NOT remove the window background
x
Great to know! Thanks @romainguy
r
Thanks for the info @romainguy, I get the idea.
But isn't the duty of the GPU overdraw is to tell if the app is doing more work then necessary? Atleast from definition it is so why it would depict something which is actually not happening? From the official documentation "So this visualization shows where your app might be doing more rendering work than necessary, which can be a performance problem due to extra GPU effort to render pixels that won't be visible to the user. So, you should fix overdraw events whenever possible." @romainguy
r
Because it indicates potentially unnecessary Views (or Composables) that may not be needed and still participate in layout computations (plus in the case of views, inflation etc.)
r
so the unnecessary surface takes part in the computation but is optimised during the darwing?
my focus is not on the surface here particularly. Talking in general other than my example. Cause I'm not using any unnecessary surfaces.