Introduction to Hermes
Facebook’s development team constantly seeks to improve the user experience of its applications by enhancing JavaScript performance on mobile platforms. After analyzing performance data, the team identified that the JavaScript engine significantly impacts startup performance and application download size.
Considering the constraints of mobile devices compared to desktops and laptops, Facebook built Hermes, a lightweight JavaScript engine designed specifically to optimize performance for React Native apps. Hermes enhances app performance on devices with limited memory, slow storage, and reduced computational power.
How Hermes Improves React Native Performance
For JavaScript-based mobile applications, key performance indicators (KPIs) influencing user experience include:
- Time to Interact (TTI): The time taken for the app to become usable after launch.
- Download Size: The overall APK size for Android applications.
- Memory Usage: Efficient use of device memory.
Hermes focuses on optimizing these KPIs by employing tailored architectural decisions, providing a significant improvement in React Native app performance.
Unlike general-purpose JavaScript engines (e.g., for browsers or Node.js), Hermes does not aim to integrate with server infrastructure or browsers. Instead, it is laser-focused on enhancing mobile app performance.
Core Architectural Decisions of Hermes
Hermes’ architecture is centered on addressing the constraints of mobile devices, such as limited RAM, slower storage, and small virtual address spaces.
1. Bytecode Precompilation
Traditionally, JavaScript engines parse the source code and generate bytecode at runtime, which delays script execution. Hermes employs ahead-of-time (AOT) compilation to generate optimized bytecode during the build process.
This approach offers several benefits:
- Faster Startup: Eliminates the need to parse and generate bytecode during runtime, reducing TTI.
- Optimized Bytecode: The AOT compiler applies whole-program optimizations, such as function deduplication and string table packing, resulting in smaller and more efficient bytecode.
- Lazy Loading: Bytecode is mapped into memory and interpreted on demand, reducing flash memory I/O and improving performance on mid- to low-tier devices.
- Reduced Out-Of-Memory (OOM) Errors: Optimized memory-mapped files can be unloaded when memory is low, mitigating OOM terminations.
Although the compressed bytecode is slightly larger than minified JavaScript, Hermes’ smaller base engine size reduces the overall APK size for Android applications.
2. No Just-In-Time (JIT) Compilation
Unlike many JavaScript engines that use Just-In-Time (JIT) compilation to improve execution speed, Hermes deliberately omits JIT for the following reasons:
- Improved TTI: JIT requires "warming up" the application during startup, which can worsen TTI.
- Lower Memory Consumption: JIT adds overhead to memory usage, negatively impacting memory-constrained devices.
- Consistent Performance: JIT is less beneficial for mobile workloads, which prioritize consistent and predictable performance over peak throughput.
Instead of JIT, Hermes focuses on enhancing the performance of its interpreter to ensure smooth execution while minimizing resource usage.
3. Garbage Collection Strategy
Efficient memory management is critical for mobile devices, especially those with limited physical memory and no OS-level swapping. Hermes employs a custom garbage collector (GC) optimized for mobile constraints:
- On-Demand Allocation: Allocates virtual memory in chunks only when needed.
- Non-Contiguous Memory: Reduces the need for a single large memory block, which is particularly important for 32-bit devices.
- Moving Objects: Allows memory compaction, releasing unused chunks back to the operating system.
- Generational GC: Reduces the time spent scanning the JavaScript heap by focusing on frequently created and destroyed objects.
These strategies minimize memory usage while maintaining responsiveness, reducing the likelihood of OOM errors and forced app terminations.
Advantages of Hermes for Mobile Applications
- Reduced Time to Interact (TTI): Precompiled bytecode and interpreter optimizations enable faster app launches.
- Smaller APK Sizes: Hermes’ compact engine design offsets the additional size of compressed bytecode.
- Optimized Memory Usage: GC strategies tailored for mobile devices reduce memory footprint and improve stability.
- Enhanced Performance on Low-End Devices: By focusing on interpreter enhancements and memory management, Hermes excels in resource-constrained environments.
Key Trade-offs and Design Choices
- No Browser or Node.js Integration: Hermes is not designed for environments like browsers or server-side Node.js, where traditional JavaScript engines may perform better.
- No JIT Compilation: While JIT can boost execution speed for specific benchmarks, Hermes prioritizes mobile KPIs such as TTI and memory efficiency over raw CPU performance.
Conclusion
Hermes is a purpose-built JavaScript engine tailored for optimizing React Native applications on mobile devices. By addressing the unique constraints of mobile environments, such as memory limitations, slower storage, and smaller APK sizes, Hermes significantly enhances performance metrics that matter most to end-users.
Through features like bytecode precompilation, JIT exclusion, and advanced garbage collection, Hermes offers React Native developers a powerful tool to build faster, smaller, and more efficient apps, ensuring a superior user experience on devices across the spectrum of performance capabilities.
For more information, visit Facebook Engineering: Hermes.