How to Optimize Your Android App Startup Time With Hermes
As you know, well-optimized performance and running at the maximum possible speed are some of the most important factors contributing to the success of mobile applications. They greatly influence how your app is perceived by users and may significantly impact its' ROI. What's more, well-performing apps achieve higher metrics in Google Play Store, which raises the ranking in in-store searches. In this article, we will show you how to achieve all these benefits with the help of Hermes.
In other blog posts based on The Ultimate Guide to React Native Optimization, we touch on the following performance-related topics:
- Optimizing your Android application’s size with Gradle Settings
- Automating your dependency management with `Autolinking`
- Debugging faster and better with Flipper
- Why is it essential to always run the latest version of React Native?
- Experimenting with the New Architecture of React Native
Be sure to check them out. Now let's jump into the main topic.
How does startup time influence the success of your app?
Users expect applications to be responsive and to load fast. However, when you’re loading a lot of Android packages during the startup time (which is unnecessary) and using an engine that is not optimized for Android, you’re unlikely to meet these expectations. In such a scenario, your application can end up receiving bad ratings in the App Store or Play Store. In the most extreme situations, it can even get abandoned in favor of the competition.
It is not easy to describe the startup time with a single metric because there are many different stages of the loading phase that can affect how “fast” or “slow” the app feels. For example, in the Lighthouse report, there are as many as six performance metrics used to profile your web application. One of them is Time to Interactive (TTI), which measures the time until the application is ready for the first interaction.
The loading process step by step
There are quite a few things that happen from the moment you press the application icon from the drawer for the first time.
As you can see in the diagram above, there are two groups of operations that influence the overall startup time of your application.
This article focuses on the first group – the improvements related to your configuration and not the business logic itself. If you have not measured the overall startup time of your application or have not played around with things such as Hermes yet - keep on reading.
Long startup times and slow UX on Android can be one of the reasons your app gets bad rating and ends up being abandoned.
Creating applications that are fun to play with is extremely important, especially considering how saturated the mobile market already is. Now, all mobile apps have to be not only easy to understand and intuitive, but also pleasant to interact with.
There is a common misconception that React Native applications come with a performance trade-off compared to their native counterparts. The truth is that with enough attention and configuration tweaks, they can load just as fast and without any considerable difference.
How to improve your app’s performance with Hermes
Note: The V8 team has recently published a blog post on improving the regular expressions’ performance. Be sure to check it out.
It’s come a long way from the bare-bones Android-only engine open-sourced in 2019, with a carefully curated set of supported JS features—due to size constraints—through finding low-size-footprint ways of adding more EcmaScript spec features, like Proxies and Intl, until making it available for macOS and iOS. Today Hermes is still small enough (~2 MB) to provide significant improvements to apps’ TTI and gives us a set of features rich enough to be used in most of the apps out there.
Before we go into the details of enabling Hermes in existing React Native applications, let’s take a look at some of its key architectural decisions.
Bytecode precompilation in Hermes reduces time to execute business logic
It can spend more time optimizing the bundle using various techniques to make it smaller and more efficient. For example, the generated bytecode is designed in a way so that it can be mapped in the memory without the eager loading of the entire file. Optimizing that process brings significant TTI improvements as I/O operations on mobile devices tend to increase the overall latency.
No JIT in Hermes translates into increased TTI
The majority of modern browser engines use just-in-time (JIT) compilers. It means that the code is translated and executed line-by-line. However, JIT compiler keeps track of warm code segments (the ones that appear a few times) and hot code segments (the ones that run many times). These frequently occurring code segments are then sent to a compiler that, depending on how many times they appear in the program, compiles them to the machine code and, optionally, performs some optimizations.
Hermes, unlike the other engines, is an AOT (ahead-of-time) engine. It means that the entire bundle is compiled to bytecode ahead of time. As a result, certain optimizations that JIT compilers would perform on hot code segments are not present.
On the one hand, it makes the Hermes bundles underperform in benchmarks that are CPU-oriented. However, these benchmarks are not really comparable to a real-life mobile app experience, where TTI and application size takes priority. On the other hand, JIT engines decrease the TTI as they need time to parse the bundle and execute it in time. They also need time to “warm up”. Namely, they have to run the code a couple of times to detect the common patterns and begin to optimize them.
How to start using Hermes on Android and iOS
If you want to start use Hermes, make sure make sure to turn the <rte-code>enableHermes<rte-code> flag in <rte-code>android/app/build.gradle<rte-code> to <rte-code>true<rte-code>:
For iOS, turn the <rte-code>hermes_enabled<rte-code> flag to <rte-code>true<rte-code> in <rte-code>ios/Podfile<rte-code>:
In both cases, whenever you switch the Hermes flag, make sure to rebuild the project according to instructions provided in the native files. Once your project is rebuilt, you can enjoy a faster app boot time and likely a smaller app size.
Note: You’ll find more insights on compiling Hermes and integrating it with React Native on our blog. If you prefer to watch or listen, we also have an entire episode of The React Native Show podcast dedicated to Hermes, so be sure to check it out.
Improved startup time means better performance and UX
Making your application load fast is not an easy task. It’s an ongoing effort and its final result will depend on many factors. You can control some of them by tweaking both your application’s configuration and the tools it uses to compile the source code.
Turning Hermes on is one of the things you can do today to improve your application’s performance drastically. Apart from that, you can also look into other significant improvements shipped by the Facebook team. To do so, get familiar with their write-up on React Native performance. It is often a game of tiny and simple improvements that make all the difference when applied at once. The React Native core team has created a visual report on benchmarking between stock RN and Hermes-enabled RN, so you might want to check it out.
As we have mentioned in the article on running the latest React Native, Hermes is one of those assets that you can leverage as long as you stay up to date with your React Native version. Doing so will help your application stay on top of the performance game and let it run at maximum speed.
Need help with performance? Give us a shout!
If you’re struggling with improving your app performance, don’t hesitate contact us. We’re the official Meta and Vercel partners, and we’ve been delivering high-quality solutions for our clients and contributing greatly to the React Native ecosystem for a while. If you’re looking for a performance optimization partner, our React Native app development company is surely the right one.