Chain React 2019: Highlights on Hermes

Elias Nahum shares his thoughts on learning about Hermes and how it can improve the Mattermost Android app.
July 18, 2019 1147 words

- Yes, yes...
- for real?
- Yes I come all the way from Taiwan... and you?
- Oh I'm from here.. and what about you
- me? hmm I flew 18 hours to get here (me)

In short, the above is how the whole thing started. It is really interesting how people from all over the globe attended the event. It was a fun learning experience and even the unexpected was waiting on us. The first day began and all those strange faces were getting a bit more familiar as everyone started to talk to each other, and most of the time the barrier came down by asking “Where are you from?”… or “Oh… that’s cool”. Slowly everyone was then talking React Native here, RN there, library over there, how did you solve this problem or the other, and so on.

Speaking about libraries, we recently decided to change from using Redux and redux-persist to Realm. By looking at Realm we found this amazing library that will let us keep the same redux pattern but using Realm under the hood; the library is called realm-react-redux but it was not being kept up to date. Basically the maintainers moved on to different projects and they have not heard of anyone using it. Boom! First surprise. We met one of the library creators and after explaining our use case they are now more than willing to merge our PRs to update the library as well as incorporate a few improvements.

Great, after and hour or so, here we go, it’s time for the conference to actually kick off. The MC was a very entertaining dude, and opened the way to our first speaker:

Improving React Native Performance (Marc Horowitz) 

Right from the start the Facebook React Native core team made a very BIG announcement: this was all about improving performance on cold start (app start), taking into consideration three primary metrics: TTI (Time to Interaction), APK (application size) and Memory (as in utilization).

So what does this mean?

🎉 Hermes welcome! 

Hermes is a new JavaScript engine designed for mobile environments that is focused on startup performance. After this short explanation by Marc, I personally went nuts! Right there and without any knowledge of what was about to happen a video pops up in the screen.

Mattermost JSC vs Herms click on the image to open the YouTube video.

BOOM BOOM POW! as you can see in the video above, the RN core team wanted to show the results on a real world app with complex interactions, layouts and functionality, and they chose Mattermost. The Mattermost app was shown as an example of what Hermes can achieve. In our app case just by switching from JSC to Hermes, cold start improved by about 50% on a Pixel XL (2016) phone.

Here more detailed results: Mattermost mobile app

And a few more comparisons thanks to Ram Narasimhan:

Ok, lets dig a little deeper and see why Hermes improves cold start. When you build a mobile app with React Native, basically all your JS code is transpiled with babel to convert your ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older environments, then the code is minified to reduce its bundle size and the bundle is saved within your app. When you run the app the JavaScript engine kicks in, parses the bundle and compiles it into bytecode and finally it gets executed. Hermes changes things a bit, your app is still transpiled with babel and then minified, but Hermes uses an ahead-of-time compiler, which runs as part of the mobile app build process. This also allows to spend more time optimizing the bytecode, and with this the bytecode is smaller in size and more efficient. Some of the optimizations that are performed include function deduplication and string table packing.

Basically with Hermes the following is done during build time: transpile with babel, minify, parse and compile to bytecode and then on run time the app just executes, meaning that it starts faster as there are less actions to take when the app first loads.

One thing that I’m not really sure about is what is the impact that using Hermes will cause to CodePush or other OTA (Over the Air) updates. The reality is that we at Mattermost do not use OTA as of now.

Conventional engine vs Hermes Hermes build and run time

No Just in Time compiler (JIT) 

Most widely used JavaScript engines like JSC use JIT to lazily compile interpreted code to machine code, but Hermes does not have a JIT compiler, meaning that it can underperform some benchmarks, especially those that depend on CPU performance. In reality those benchmarks in general are not representative of mobile application workloads, so we should be good with this. What we need to understand is that conventional engines that use JIT need to warm up when the application starts and that has an impact with TTI (Time to interaction). Also, let’s not forget that JIT will also add to code size and memory consumption that will also have a negative impact in cold start. Basically there are some trade-offs by using Hermes instead of JSC or other conventional engines, but the interpreter performance should be good enough.

What does this mean during development 

When developing your app, Hermes will not use ahead-of-time compilation, but instead it will generate the bytecode lazily on the device, allowing rapid interaction with Metro. Obviously this means that the lazy-compiled bytecode will not include all the optimizations of a production build. In theory you should expect a more performant app in production than in development.

Another thing to consider is that Hermes focused on ES6 specifications and it should be kept up to date as new specifications emerge. However, one important thing is that because the goal was to keep the size small, some of the language features are not included such as proxies and local eval(). Here is a complete list of unsupported features.

We do use Reflect in various places throughout our code base at Mattermost, and it is one of the unsupported features of JS. Thus, in our case we will need to either use a polyfill or make changes appropriately.

Conclusion 

Hermes was a great surprise: we know we’ve been doing a lot of work trying to improve cold start on Android, we implemented RAM Bundles (previously known as Unbundling), we use inline requires, we use lazy load of native modules with the new TurboReactPackage, and whatever other strategy we can think of. So we are definitely looking forward to add Hermes to our Android React Native app and probably contribute back to Hermes those features that we need in order to avoid the use of polyfills. And still looking forward to the release of Fabric and TurboModules 😁


Written by Elias Nahum - @elias on community.mattermost.com and @enahum on GitHub

Join us on community.mattermost.com!