Unlocking Jetpack Compose's Rendering Performance: A Comprehensive Analysis - Part 1

Unlocking Jetpack Compose's Rendering Performance: A Comprehensive Analysis - Part 1

How jetpack compose renders under the hood, how it's performance veries, lets see in-depth.

When we hear Jetpack Compose, we think it is the fastest. But the problem is to get the most responsive and fastest output from the jetpack compose UI, it should be rendered fast. But for this, we need some pre-requisites to complete to get the most out of it. Several factors affect jetpack compose render time. All of them are specified below for easy understanding.

On Pre Render Basis:

There are mainly two kinds of rendering scenario a jetpack compose can show different performance:

  1. Without any pre-render of components

  2. With pre render of components

Without pre rendering of components:

Jetpack compose UI mainly consists of some UI components like text view, list, columns, buttons, etc. Whenever first time we need to launch a project it takes more time to render each component the first time. If it renders once in a project, then it takes minimal time afterward. Suppose we have an A and B screen. Now whenever we launch A screen it takes a longer time to render it first and for B screen it will take less time because the maximum components used in B Screen are already rendered. There are some speed comparisons for this,

Frozen frame percentage:

Without Pre Render: For compose-based layout, the frozen frame rate came out to be 0.044% whereas it was 0.034% for XML-based layout.

With Pre Render: For compose-based layout, the frozen frame rate came out to be 0.018% whereas it was 0.044% for XML-based layout.

Slow frame percentage:

Without Pre Render: For compose-based layout, the slow frame rate came out to be 8.91% whereas it was 9.93% for XML-based layout

With Pre Render: For compose-based layout, the slow frame rate came out to be 10.29% whereas it was 10.7% for XML-based layout.

Median Page Load Duration (in milliseconds):

Without Pre Render: Compose-based layout nearly took 2.5x the time than the XML-based layout.

Without Pre Render: Compose-based layout nearly took 1.6x the time than the XML-based layout.

So, it is clear if you make a project with jetpack compose you should get overall performance boost for whole project. But it will always take longer time for the first screen to initialize.

Build Type Specific:

The speed of render also can be highly effected by building the application. There are mainly 3 things we need to care when we work with jetpack compose.

  1. Using R8: Without r8 enabling the render speed of jetpack compose view's decrease severely. It becomes about 20% slower when we build without enabling R8.

  2. Debug Build: Jetpack compose performance is worse in debug applications. When r8 is disabled and running on debug mode, a compose view can render 2X or sometimes 3X slower then regular.

  3. Compose in XML: When we use compose view on XML it also reduces the render speed about 5 to 10% depending on devices.

  4. Compose in RecyclerView: When we use compose view in RecyclerView it also reduces the render speed about 10% to 20% depending on devices.

So, whenever we works with compose these specific factors should be considered. We also should ensure we are using R8 in the project when we use jetpack compose. Also to test compose project, we should use release and r8 enabled build to ensure best output from the project while testing.

By View Lifecycle Specific:

Compose is highly dependent on two lifecycle, one is onWindowFocusChange and onResume. The behavior of compose in onResume is similar to XML and 40% to 50% faster then normal XML views. But when we take onWindowFocusChange() into calculation it changes the scenario drastically. It can take up to 2X time even 3X,4X when use compose and XML together.

In conclusion Compose turned out to be 50% faster than xml in the onPause() method, but 2 times slower in the onWindowFocusChanged() method.

By View Recompose

When we create a complex compose view with multiple nested views we should make sure we only recompose the specific view always. Else it will recompose the whole view.

This can happen in these cases:

  1. Multiple State Views: When a view depends on multiple states, we always control the changes in the child views and finally update the whole state on the parent view. Thus we can maximize the performance and minimize the recomposition, flickering on main parent view.

  2. When using List and LazyColumn: We should always remember the exact mutable view and recompose that view upon changes in a list. Else it will recompose the whole list that will take extra render time

  3. Finite Nested List: Always try to use finite nested list and say the maximum or minimum size of nested list in the parent view. Because if we do not specify it, that will take extra time and sometimes in lower end devices it can cause crashes.

  4. Optimize Navigation Mapping: When we navigate through a compose application, we should optimize the navigation mapping and graph. We should always take care when to replace, recreate or recompose a view.

Conclusion:

There are many core things that are not discussed in this article. But I tried to cover all the basic compose view rendering cases. In the part 2, I will bring all the in depth reason's why these are happening and how to prevent it with example. Thank you very much. If any question let me know.