icon

Petr Introvič

5. 6. 2023

Boosting App Performance with Baseline Profiles

Originally posted at now non-existing Showmax Engineering blog hence the reupload here.

At Showmax, we understand that the success of an app heavily depends on its ability to provide a smooth and efficient user experience. While an aesthetically pleasing design is important, it's equally essential to prioritize the app's performance. After all, even the slightest inconvenience such as crashes or frozen frames can drive users away. That's why we were intrigued when we first heard about Baseline Profiles and their bold claim to "make apps blazing fast". Before we dive into how Baseline Profiles can help improve app performance, let's first take a look at the evolution of code execution on Android and the challenges developers face in optimizing their apps for speed.

Interpreted code in Android 1.0

In the first version on Android back in 2008, code execution was entirely interpreted, meaning that no optimization was performed, and the code needed to be translated into bytecode for every instruction which resulted in low app performance. However, this approach didn't require additional disk space, which was a major consideration at the time.

Android 2.2 and Just In Time compiler

With the release of Android 2.2 Froyo in 2010, the Just In Time (JIT) compiler was introduced, which optimized the most frequently executed parts of the code to improve app performance. However, the optimizations were lost after the app process was killed, resulting in slower startup times.

From JIT to Ahead Of Time compiler

The Android 5 Lollipop update in 2014 introduced a full Ahead Of Time (AOT) compiler, which optimized the entire app ahead of time, resulting in faster startup times but longer loading times due to the larger size of the optimized code.

Once loaded, the app’s performance was fast. However, every time the system or the app was updated, the optimization had to be run again for every single device, which was time-consuming and resource-intensive.

Back to JIT with profile guided optimizations

Android version 7 Nougat from 2016 returned to JIT and added a Profile Guided Optimization (PGO) where the hot paths were stored and then (usually overnight) optimized via a tool called dex2opt. This approach meant that the app would get faster after every use, but only for the parts that were actually used.

Profile guides in cloud

The last significant change came in 2018 with the release of Android version 9 Pie which added Cloud profiles. Cloud profiles work by gathering and uploading profiles for a particular app and a device to the Play store, where they are aggregated and distributed during installation to other devices and applied. This approach significantly improves app performance, but it takes days or weeks to gather the profile, and every new version/update means starting from scratch.

At Showmax, we update our Android app every few weeks, meaning that the optimizations are gathered from scratch after every update. While our users may eventually benefit from the optimization profiles distributed through Cloud profiles, as a result, the time it takes for the profiles to become available means that our users may not experience the full benefits of the optimized performance for several days or weeks after each update.

In the next section, we'll explore how Baseline Profiles can help us address these challenges and achieve faster app performance.

VesionTypeInitial pefEventual pefDisk space
1.0IntepetedSlowSlowSmall
2.0 FoyoIntepeted + JITSlowFastSmall
5.0 LollipopFull AOTFast-ishFastLage
7.0 NougatJIT + PGOSlow / FastFastSmall-ish
9.0 PieCloud pofilesSlow-ish / FastFastSmall-ish

Table is inspired by the presentation "Making apps blazing fast with Baseline Profiles" by Rahul Ravikumar and Tomás Mlynaric presented at the Android Dev Summit.

Baseline Profiles

Baseline Profile to the rescue. What does it offer?

Baseline Profiles allow us to generate the profile used for app optimisation beforehand. The generated profile (which is just a list of used methods to be optimized) is then included in the app installer. From there, it is distributed with each install or update via the Play Store to every user.

The optimizations are then ready immediately.

The benefits of Baseline Profiles are clear: Fast initial and eventual performance, the disk space requirements are still small-ish, and most importantly, it is ready immediately after every install or update. No need to wait for app usage or cloud profiles distribution through the Play store.

VesionInitial pefEventual pefDisk space
7.0+FastFastSmall-ish

Implementation

Implementation and usage is pretty straightforward and well-documented in the official documentation.

It consists of:

  • Adding dependencies for the Macrobenchmark library and setting up the Macrobenchmark module.
    • Adding a test for generating a Baseline Profiles
      @get:Rule val baselineProfifileRule = BaselineProfifileRule()
      
      @Test
      fun startup() = baselineProfileRule.collectBaselineProfile(
       packageName = "com.example.app",
       profileBlock = {
         startActivityAndWait()
         // Interact with the app if you want
       }
      )

      The test run output has a generated file of a Baseline Profile which can then be bundled with the app and together with the added dependency of androidx.profileinstaller it is ready to go!

      Ready to go means that it is included in the app build files and should be automatically used for optimizing the app when the app is installed or updated from the Play Store.

      For developers, there is also a way to install an app with the Baseline Profile locally.

      Does it really work?

      Our dataset contains a large number of collected Baseline Profile usage numbers. Below are the values from tests that appear to be the most average and reliable.

      The first table shows the median of cold app startup with and without Baseline Profile for our common testing devices. We see a 23 % and 33 % decrease of app startup time.

      Cold app startup in ms, median of 10 iterations

      Device typeNo pofileBaselineDecease
      Regula pefomance (Pixel 2XL)1 119.0921.0- 23 %
      Low pefomance (Stong AGT419)4 135.92 757.0- 33 %
      Refeence (develope device)(ADT-3)2 687.62 137.1- 21 %

      The second table shows data from measuring app performance of scrolling content on our home screen – the main screen where users can browse a large catalog of assets. It consists of a scrolling column with different view types, the most typical one is a row of curated assets (many vertically and horizontally scrollable views). The measured values were frame durations within the 95th percentile. We see an improvement of over 30 % on both tested devices.

      Home scrolling performance, 95th percentile of frame durations in ms, 10 iterations

      Device typeNo pofileBaseline pofileDecease
      Regula pefomance (Pixel 2XL)48.529.0- 40 %
      Highe end device (Pixel 5)21.914.7- 33 %

      Really?

      So, do the numbers lie or not? The video comparison clearly shows that Baseline Profiles significantly improve app performance. On the right side of the screen, where the Baseline Profile is implemented, the app starts up faster, responds quicker, and the scrolling on the home screen is much smoother.

      Check out the video:

      Summary

      As we've seen, the evolution of code execution on Android has been a rollercoaster ride, with different optimization techniques being used in different versions of the operating system.

      Incorporating Baseline Profiles into your app is a relatively straightforward process, and the benefits are clear. In our experience at Showmax, we've seen startup time decrease by 20 - 30 %, and the eventual performance improvement by around 35 %. The best part is that these optimizations are available immediately after every app install or update, without any delay.

      All it takes is to add one test to record a Baseline Profile and the results of that to your project. By implementing this technique, you can ensure that your app provides a seamless user experience, with faster startup times and smoother performance.

      In conclusion, code execution on Android has come a long way, and with the introduction of Baseline Profiles, developers have yet another tool to optimize app performance. By incorporating this technique into our app at Showmax, we've seen significant improvements in performance, and we encourage other developers to explore the benefits of Baseline Profiles for their own apps.

      8x

      About Petr Introvič

      As an Android developer started at Slevomat.cz - a discount platform for experiences and shopping, then moved to a world of streaming services. First to Showmax.com - a an African entertainment service which then was assimilated and now belongs to group with US Peacock and European SkyShowtime, for a full global reach, which he develop together under Sky Czech Republic.
      Other interests includes Ruby on Rails, Kotlin Multiplatform, Flutter, Bitcoin and Floorball.
      Back to all posts