Skip to content

[Performance] Mounting is very slow on Android (almost 1 ms per one view) #51869

@tomekzaw

Description

@tomekzaw

Description

Hello, this is Tomasz from the Reanimated team at Software Mansion. We're getting numerous issue reports from our library users who complain about poor performance of our library on Android when the New Architecture is enabled. Most of the app developers state that performance used to be better on the Legacy Architecture so we consider this to be a regression of enabling the New Architecture in their apps.

During our investigation, we found out that one of the bottlenecks is rooted in the Fabric implementation itself. Reanimated uses the exact same way to update view props and styles as React Native does. Reanimated commits all changes by calling ShadowTree::commit method in C++ (on the UI thread) and relies on the rest of the New Architecture rendering pipeline, including the mounting manager.

To demonstrate the behavior, I prepared a simple reproducer without react-native-reanimated that simply uses React state and React renders to update the rotation of 400 views. While I understand that there are no easily explainable use cases for updating the rotation of 400 views at once (which takes ~300 ms in a debug mode), based on this we can calculate how long it takes to mount updates for a single view (almost a 1 ms, or ~0.75 ms to be exact). Considering that nowadays most of smartphones run at 60 or even 120 fps and there's only 8 ms to render each animation frame, this means we can update only up to 10 views during a single animation frame which is far below reasonable expectations.

Image

I've also measured how long it takes to update only {opacity} (~90 ms), {transform:[{rotate}],[{scale}]} (~400 ms) and {opacity,transform:[{rotate}]} (~350 ms).

When it comes to transforms, keep in mind that rotation is stored inside a single key-value pair object inside a single-item array so most likely this type of data structure takes more time to be serialized (especially if JNI calls are involved). This however still doesn't explain why it takes 0.2 ms in the debug mode to update only the opacity of a single view.

Image

Just for comparison, I've also run some measurements in the release (profileable) build. It looks like mounting is ~2x faster than in debug mode. Keep in mind though, that most of the developers use a debug build to develop their app and implement animations so can't really advise them to "just use a release build".

Image

Steps to reproduce

  1. Install the dependencies with yarn install
  2. Open the application with Android Studio with open -a "Android Studio" android
  3. Build the application in the debug mode and launch it on a low-end Android device, e.g. OPPO A16
  4. Open "Profiler" tab in Android Studio
  5. Select "Capture System Activities / System Trace"
  6. Press "Start anyway" button in Android Studio to start recording
  7. Press "Set rotation" button several times in the reproducer app
  8. Press "Stop recording and show results"
  9. Scroll down to "m.reproducerapp" row and click on the label
  10. Zoom in to the traces from a single rotation update using WASD keys
  11. Hover over "IntBufferBatchMountItem::mountInstructions::UPDATE_PROPS numInstructions=400"
  12. See the running time, e.g. "Running: 337.16 ms"

React Native Version

0.79.3

Affected Platforms

Runtime - Android

Areas

Fabric - The New Renderer

Output of npx @react-native-community/cli info

System:
  OS: macOS 15.5
  CPU: (12) arm64 Apple M3 Pro
  Memory: 109.67 MB / 18.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.19.0
    path: ~/.nvm/versions/node/v18.19.0/bin/node
  Yarn:
    version: 3.6.4
    path: ~/.nvm/versions/node/v18.19.0/bin/yarn
  npm:
    version: 10.2.3
    path: ~/.nvm/versions/node/v18.19.0/bin/npm
  Watchman:
    version: 2025.05.26.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.16.2
    path: /Users/tomekzaw/.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.5
      - iOS 18.5
      - macOS 15.5
      - tvOS 18.5
      - visionOS 2.5
      - watchOS 11.5
  Android SDK:
    API Levels:
      - "30"
      - "31"
      - "33"
      - "34"
      - "35"
    Build Tools:
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 33.0.1
      - 34.0.0
      - 35.0.0
    System Images:
      - android-33 | Google APIs ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-35 | Google Play ARM 64 v8a
      - android-36 | Google APIs ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.3 AI-243.26053.27.2432.13536105
  Xcode:
    version: 16.4/16F6
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.15
    path: /usr/bin/javac
  Ruby:
    version: 3.2.2
    path: /Users/tomekzaw/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 18.0.0
    wanted: 18.0.0
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.3
    wanted: 0.79.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: false

Stacktrace or Logs

No stacktrace

MANDATORY Reproducer

https://github.com/tomekzaw/reproducer-android-slow-mounting

Screenshots and Videos

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions