DEV Community

Cover image for React Native's New Architecture: An Overview of Performance Benchmarks
Yoel
Yoel

Posted on

React Native's New Architecture: An Overview of Performance Benchmarks

React Native is on the brink of a significant transformation. The development team has announced plans to set the New Architecture as the default by the end of 2024. This strategic shift is poised to enhance how developers build and maintain React Native applications.

"The team plans to enable the New Architecture by default in an upcoming React Native release by the end of 2024."

The anticipation within the industry is palpable. As the New Architecture promises improved performance and flexibility, a majority of libraries are actively updating to ensure compatibility with the new system. As of May 2024, an impressive 60% of the top 400 popular libraries have already made the transition,You can view the process and statistics in our community discussions on GitHub.

For a detailed look at the adoption rate across various libraries, refer to comprehensive list available on Google Docs. This document provides an up-to-date view of the migration process and showcases the widespread commitment to adopting the New Architecture.

Google Docs

How to Test Your App with New Arch

To explore the New Architecture features in your app, enable it by setting newArchEnabled to true in the gradle.properties file for Android. For iOS, use this command:



RCT_NEW_ARCH_ENABLED=1 bundle exec pod install


Enter fullscreen mode Exit fullscreen mode

To streamline the setup, add this script to your package.json file:



"pod-install": "cd ios && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install"


Enter fullscreen mode Exit fullscreen mode

This allows you to execute the command without typing it manually each time.

Understanding the New Bridge System

The New Architecture introduces a revamped bridge system to connect native modules with JavaScript more efficiently. In this system, the native modules can directly return results synchronously, unlike the older architecture, which relied on promises and asynchronous operations. Let's look at an example:

Defining a Native Module

In JavaScript, a native module can be declared like this:



import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  multiply(a: number, b: number): number;
}

export default TurboModuleRegistry.getEnforcing<Spec>('TestNewTurboModule');


Enter fullscreen mode Exit fullscreen mode

To use this module:



const TestNewTurboModule = require('./NativeTestNewTurboModule').default;

const App = () => {
const result = TestNewTurboModule.multiply(2, 3);
    console.log(result); // Logs the result synchronously
}


Enter fullscreen mode Exit fullscreen mode

Note: Using require ensures that the native module is loaded lazily, optimizing resource usage by only loading it when needed.

You can see the mechanism in action in code

Implementing on the Native Side

In the new architecture, you can directly return values like so:



@ReactModule(name = TestNewTurboModuleModule.NAME)
class TestNewTurboModuleModule(reactContext: ReactApplicationContext) :
  NativeTestNewTurboModuleSpec(reactContext) {

  override fun getName(): String {
    return NAME
  }

  override fun multiply(a: Double, b: Double): Double {
    return a * b
  }

  companion object {
    const val NAME = "TestNewTurboModule"
  }
}


Enter fullscreen mode Exit fullscreen mode

This differs from the older architecture, which used promises to return values asynchronously.



//Old NativeModules
import { NativeModules } from 'react-native';
const { TestOldModule } = NativeModules;

const getResult = async () => {
const result = await TestOldModule.multiply(3, 7);
};

Enter fullscreen mode Exit fullscreen mode




Compatibility Backwards

When integrating the New Architecture into your app, a critical aspect to consider is the compatibility of existing native modules or libraries developed with the old architecture. How does the New Architecture handle these?

Introducing the Interop Layer

Support for Legacy Native Modules

Since we no longer have access to a bridge in Bridgeless mode, we've introduced an interoperability layer to continue support for legacy native modules.

With the interlop layer, most legacy modules registered with React Native will be supported by the new native module system (TurboModules).

The interop layer is a strategic enhancement included with React Native starting from version 0.72, designed to facilitate the reuse of legacy native components within New Architecture apps without requiring immediate migration. This feature initially required developers to register components explicitly, as discussed in the React Native Working Group's documentation. However, starting from version 0.74, this registration process has been automated, simplifying the integration process significantly. More details can be found in a more recent discussion.

If your application includes older native modules, it's essential to verify their compatibility with the New Architecture, particularly with the introduction of "Bridgeless Mode." This mode eliminates the traditional bridge system, replacing it with an interoperability layer that supports legacy modules. It's important to note that some adjustments might be necessary to ensure full compatibility. For example, due to the TurboModule's implementation of lazy-loading methods, you can no longer use the spread operator on native modules. Instead, you should use Object.create. Further guidance is available in the compatibility guide.

Bridgeless Mode

What is Bridgeless Mode?

In the evolution of React Native, the traditional bridge played a pivotal role by managing the communication between JavaScript and native components. This bridge operated as a messaging queue, handling tasks such as rendering views, invoking native functions, or managing event handlers. However, it also imposed limitations due to its inherently asynchronous nature, which involved message batching and serialization costs.

The introduction of Bridgeless Mode in version 0.73 marked a significant milestone, initially available as an optional feature in the New Architecture. This mode fundamentally changes the dynamics of React Native's internal mechanics by eliminating the old bridge system. For more details on the initial implementation in version 0.73, refer to the Introducing Bridgeless Mode 🎉.

As of version 0.74, Bridgeless Mode has become the default setting in the New Architecture, reflecting a shift towards more direct and efficient communication pathways between JavaScript and native code. This transition is detailed in a subsequent Bridgeless By Defaul, highlighting the continued enhancements and the commitment to optimizing React Native's performance.

Performance Enhancements in the New Architecture

The React Native team has emphasized significant performance improvements with the New Architecture, designed to streamline the interaction between JavaScript and native code by overcoming the constraints of legacy systems. As this architecture progresses toward full stability, developers are encouraged to start their migration and testing early. This proactive engagement is crucial for optimizing performance and enhancing the flexibility of app development processes.

Code Snippet from the Performance Test



const TIMES_TO_CHECK = 100000;

React.useEffect(() => {
const startTime = performance.now();
for (let i = 0; i < TIMES_TO_CHECK; i++) {
const result = check(); // Call check function TIMES_TO_CHECK times
}

const endTime = performance.now();
setExecutionTime(endTime - startTime); // Calculate the time taken to execute 1000 calls
}, []);

Enter fullscreen mode Exit fullscreen mode




Reproducing the Benchmarks

To ensure transparency and allow for independent verification of the results presented in this article, I have made the source code for the performance benchmarks available on GitHub. Whether you're a developer interested in the technical details or just curious about the testing methodology, you can access and run the benchmarks yourself.

For those interested in the performance improvements with the new architecture, visit the repository for the new module:

For a comparative perspective with the old architecture, check out the repository for the old module:

Each repository includes detailed instructions on how to set up and run the tests on both iOS and Android platforms, ensuring you can replicate the findings reported in this article.

In-Depth Performance Analysis of Native Modules

React Native utilizes two primary types of native modules:

  • Native Modules: These are general-purpose modules that encapsulate custom functionality, enhancing the core capabilities of an app.
  • View Modules (Fabric in New Architecture): Specifically designed to manage UI components, these modules play a critical role in rendering and interface operations.

To quantitatively evaluate the performance gains offered by the New Architecture, I conducted a series of benchmarks focusing on native modules. These tests were designed to measure the frequency at which a function could be called and successfully return a response within a set time frame, providing a clear metric of responsiveness and efficiency.

Methodology: Using a custom-developed native module configured to return a Boolean value, I tracked the number of successful calls within specific time intervals. This method ensures an accurate representation of performance enhancements.

Benchmark Results:

Invocation Count Old Architecture (ms) New Architecture (ms) Percentage Improvement (%)
1,000 759 3 99.60%
10,000 8,308 40 99.52%
50,000 44,342 174 99.61%
100,000 85,011 342 99.60%

The results demonstrate a substantial boost in the speed and responsiveness of native module operations within the New Architecture. This enhancement is pivotal, reducing latency and significantly improving the fluidity of app functionalities, which is especially beneficial in high-load scenarios.

As we continue to explore the advancements in React Native, the following section will delve into the performance improvements specific to view modules, also known as Fabric in the New Architecture, highlighting how they contribute to an overall enhancement in UI rendering and interaction.

Performance Benchmark Analysis of View Modules

For a detailed understanding of the performance improvements in view module rendering within the New Architecture, I recommend reviewing the comprehensive study conducted by Samuel Susla. This research meticulously measures the rendering times by marking timestamps at the initiation of an update and when the update is visually reflected on the screen. The full discussion and findings can be found in this GitHub post.

The benchmarks were conducted on physical devices to accurately reflect real-world usage. Below are the summarized results illustrating the comparative performance between the Old and New Architectures across different component types and volumes.

Performance on Google Pixel 4

Component Type Number of Components Old Architecture Time (ms) New Architecture Time (ms) Performance Difference
View 1,500 282 258 ~8% faster
View 5,000 1088 1045 ~4% faster
Text 1,500 512 505 ~1% faster
Text 5,000 2156 2089 ~3% faster
Image 1,500 406 404 similar
Image 5,000 1414 1370 ~3% faster

Performance on iPhone 12 Pro

Component Type Number of Components Old Architecture Time (ms) New Architecture Time (ms) Performance Difference
View 1,500 137 117 ~15% faster
View 5,000 435 266 ~39% faster
Text 1,500 324 284 ~13% faster
Text 5,000 1009 808 ~20% faster
Image 1,500 212 172 ~19% faster
Image 5,000 673 451 ~33% faster

These benchmarks clearly demonstrate the performance enhancements brought by the New Architecture across various scenarios. Significant improvements are particularly notable in environments with high component counts, showcasing the New Architecture’s ability to handle complex rendering tasks more efficiently. This results in faster app responsiveness and a smoother user experience.

Ready for the Future: Engage with the New Architecture Today

As we approach the wider implementation of the New Architecture in React Native, it is clear that the transition is more than just a technical upgrade—it's a community effort. The React Native team is committed to supporting the developer community through this transition. With dedicated resources aimed at resolving potential issues related to adopting the new architecture, the team is actively working in close cooperation with developers to ensure a smooth migration.

Although the New Architecture is not yet fully stable, the benefits of early adoption are significant. Starting your migration plans now allows you to identify and address any challenges or blockers your app may encounter early in the process. The year 2024 presents a prime opportunity to experiment with the New Architecture, seek support, provide feedback, and influence the final adjustments before its widespread release.

By engaging now, you not only prepare your projects for future advancements but also contribute to a broader effort that will define the next standard in mobile application development. Let’s embrace this journey together, fostering a robust and forward-thinking React Native ecosystem.

Acknowledgments

A special thanks to the Builder Bob library for facilitating the creation of native modules swiftly and with minimal effort. Additionally, gratitude is extended to Flashlight for providing the tools necessary to measure app performance effectively.

For more insights and continuous updates on React Native and other technologies, follow me on Twitter, connect with me on LinkedIn.

Top comments (0)