preface

Compose has recently been released in version 1.0, which shows that Google considers Compose ready for use in formal production environments. How does Compose perform compared to traditional XML?

In this article, Compose’s performance is analyzed from two aspects: build performance and runtime. The data comes from: Measuring Render Performance with Jetpack Compose – Before and after – Measuring Render Performance with Jetpack Compose

Building performance

Tivi is an open source movie App. Tivi was originally built based on Fragment and XML, and used DataBinding and other frameworks that used annotation processors. Later, tivi migrated to Compose for UI construction

  1. Step 1: Migrate toNavigationwithFragment, eachFragmenttheUIBy theComposebuild
  2. Step 2: RemoveFragmentCompletely based onComposeimplementationUI

Now let’s compare and analyze the performance of pre-compose,Fragments + Compose, and Entirely Compose

APKvolume

Package volume is one of the performance metrics we always focus on. Let’s take a look at the package volume comparison for the three phases





As you can see,TiviAPKReduced in size46%, from4.49 MBCut to2.39 MB, and the number of methods is also reduced17%

It is important to note that when you first start using Compose in your application, you will sometimes find that the APK size actually increases. This is because the migration is not complete. The old dependencies have not been removed and new dependencies have been added, resulting in a larger APK size and a smaller APK size when the project is fully migrated to Compose. And better than the original indicators.

Lines of code

We know that lines of code are not a particularly useful statistic when comparing software projects, but it does provide an observation of how things are changing. We use cloC tools to count lines of code

cloc . --exclude-dir=build,.idea,schemas
Copy the code

The results are shown below:

As you can see, the migration toComposeAnd then, not surprisingly,XMLThere are fewer lines of code76%

It is interesting to notekotlinThe code is also reduced, probably because we can reduce a lot of template code and also remove some of the code we wrote earlierView Helpercode

Build speed

As projects get bigger, build speed is a metric that developers increasingly care about. Before we started refactoring, we knew that removing a lot of annotation handlers would help speed up the build, but we weren’t sure how much.

We run the following command five times and take the average

./gradlew --profile --offline --rerun-tasks --max-workers=4 assembleDebug
Copy the code

The results are as follows



The consideration here is debug build time, which you will be more concerned with during development.

Tivi’s average build time before migrating to Compose was 108.71 seconds. With full migration to Compose, the average build time was reduced to 76.96 seconds! Build time was reduced by 29%. Compose is not the only contributor that can shorten the build time by this much, but is largely due to two factors:

  1. One is to remove the annotation handlerDataBindingandEpoxy
  2. The other one isHiltinAGP 7.0Is faster.

Runtime performance

Now that we’ve seen Compose’s performance at build time, let’s take a look at how well Compose performs at run time

Preparation before analysis

When using Compose, you can have several metrics that affect performance

  • If we were completely inComposeIn the buildingUIMeeting how?
  • If we’re using a complex viewCompose(e.g., useLazyColumnreplaceRecyclerViews), but the root layout is still added inXMLIn the
  • If we useComposeWhat about replacing elements of a page, rather than the entire page?
  • Whether it can be debugged andR8How much does the compiler affect performance?

To begin answering these questions, we built a simple test program. In the first version, we added a list of 50 elements (about 12 of which were actually drawn). The list includes a radio button and some random text.



To test the impact of various options, we added the following four configurations, all of which are enabledR8It’s closed at the same timedebug

  1. Pure Compose
  2. aXMLIn, with only oneComposeView, the specific layout is written inComposeIn the
  3. XMLContains only oneRecyclerView, butRecyclerViewEach term of phi is oneComposeView
  4. pureXML

To test the impact of build type on performance, the following three configurations were added

  1. pureCompose, close theR8And open thedebug
  2. pureCompose, close theR8And close thedebug
  3. pureXML, close theR8And open thedebug

How do you define performance?

The Compose runtime performance is generally understood as the time between the page launching and the user seeing the content, so the following times are important for us

  1. ActivityStartup time, i.eonCreate
  2. ActivityStartup completion time, i.eonResume
  3. ActivityThe rendering completion time is the time the user sees the content

The timing of onCreate and onResume is easy to master by overriding the system methods, but how do you get the time when your Activity is fully drawn? We can add a ViewTreeObserver to the page root View and record the time of the last onDraw call

Using the Profile to view the process described above, the following is the process of rendering using XML versus composing, from OnCreate to the last call to onDraw



Rendering performance analysis

Now that we know how to define performance, can we start testing

  1. Each test ran on several devices, including the most recent flagship, NoGoogle PlayService devices and some cheap cell phones.
  2. Each test was run 10 times on the same phone, so we could get not only first render times, but also second render times
  3. testComposeVersion for 1.0.0

We ran several times according to the configuration defined above, and got some data. Interested students can directly view all the data



As the results of the analysis are shown above, we can draw some conclusions

  • R8And whether it can be debuggedJetpack ComposeRendering time has a significant effect. In each experiment, disableR8And debuggability enabled builds took more than twice as long as builds without them. On our slowest device,R8Turns rendering speed up by more than half a second while disableddebugSpeed up rendering by half a second.
  • XMLContains only oneComposeViewRender time, with pureComposeAbout the same amount of time
  • RecyclerViewContains more than oneComposeViewIt’s the slowest. This is not surprisingXMLThe use ofComposeViewThere is a cost, so it’s used in the pagesComposeViewLess is better.
  • XMLIn terms of presentationComposeFaster. There is no way to solve this problem, and in each case,ComposeRender time ratioXMLGrow by about 33%.
  • The first startup always takes longer to render than subsequent launches. If you look at the full data, the rendering time of the first page is almost double that of the subsequent ones.

Somewhat to my surprise, although Compose does not have IO for converting XML to View and the measurement process is made more efficient by the inherent property measurement, the performance is still worse than that of XML. However, according to Leland Richardson, when installing an application from Google Play, Thanks to bundled AOT compilation, Compose renders faster at startup, further narrowing the gap with XML

conclusion

The above analysis of Compose performance is summarized as follows

  1. If you completely migrate toComposeIn terms of package size, lines of code, compilation speed and so on, there should be a big improvement
  2. If you are in a migration phase, you may introduce new dependencies instead of removing old ones, which may lead to larger packages
  3. Even though it’s goneXMLThe conversionIOThe operation, the measurement process is also optimized with intrinsic property measurements,ComposeCompared to the rendering performanceXMLThere is still a gap
  4. Despite the currentComposeSlightly lacking in performance (just over a frame or two on most devices), but due to its impact on developer productivity, code reuse, and declarativeUIThe advantages of powerful features,ComposeStill recommended

The resources

Jetpack Compose — Before and after Measuring Render Performance with Jetpack Compose