From d05142d3cc6fed61ae9ecd456bec84a9369fdf5f Mon Sep 17 00:00:00 2001 From: Shubh Porwal Date: Sat, 26 Sep 2026 12:27:07 +0530 Subject: [PATCH] Write executeMount int buffer to Java in a single JNI call executeMount called SetIntArrayRegion for every writeInt/writeIntArray, ~4.5k JNI calls for a commit mounting 1500 views. The exact size is already known from computeBufferSizes, so fill a std::vector and copy it once before createIntBufferBatchMountItem. The int[] Java receives is unchanged. Changelog: [ANDROID] [CHANGED] - Write Fabric mount instruction ints to Java in a single JNI call Co-Authored-By: Claude Opus 5.5 (1M context) --- .../react/fabric/FabricMountingManager.cpp | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp index 0ed7581b4c38..fdbd0999f850 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp @@ -12,6 +12,7 @@ #include "StateWrapperImpl.h" #include +#include #include #include #include @@ -359,22 +360,18 @@ jni::local_ref getProps( } struct InstructionBuffer { - JNIEnv* env; - jintArray ints; + std::vector ints; jni::local_ref> objects; - int intsPosition = 0; int objectsPosition = 0; inline void writeInt(int value) { - env->SetIntArrayRegion(ints, intsPosition, 1, &value); - intsPosition += 1; + ints.push_back(value); } template inline void writeIntArray(const std::array& buffer) { - env->SetIntArrayRegion(ints, intsPosition, N, buffer.data()); - intsPosition += N; + ints.insert(ints.end(), buffer.begin(), buffer.end()); } inline void writeObject(jobject obj) { @@ -863,10 +860,9 @@ void FabricMountingManager::executeMount( // Allocate the intBuffer and object array, now that we know exact sizes // necessary InstructionBuffer buffer = { - .env = env, - .ints = env->NewIntArray(batchMountItemIntsSize), .objects = jni::JArrayClass::newArray(batchMountItemObjectsSize), }; + buffer.ints.reserve(batchMountItemIntsSize); // Fill in arrays int prevMountItemType = -1; @@ -996,6 +992,13 @@ void FabricMountingManager::executeMount( } } + // Copy the ints to Java in a single JNI call, rather than one per write + react_native_assert( + static_cast(buffer.ints.size()) == batchMountItemIntsSize); + jintArray ints = env->NewIntArray(static_cast(buffer.ints.size())); + env->SetIntArrayRegion( + ints, 0, static_cast(buffer.ints.size()), buffer.ints.data()); + static auto createMountItemsIntBufferBatchContainer = JFabricUIManager::javaClassStatic() ->getMethod( @@ -1006,7 +1009,7 @@ void FabricMountingManager::executeMount( surfaceId, // If there are no items, we pass a nullptr instead of passing the // object through the JNI - batchMountItemIntsSize > 0 ? buffer.ints : nullptr, + batchMountItemIntsSize > 0 ? ints : nullptr, batchMountItemObjectsSize > 0 ? buffer.objects.get() : nullptr, revisionNumber); @@ -1026,7 +1029,7 @@ void FabricMountingManager::executeMount( telemetry.getAffectedLayoutNodesCount(), static_cast(synchronous)); - env->DeleteLocalRef(buffer.ints); + env->DeleteLocalRef(ints); } void FabricMountingManager::drainPreallocateViewsQueue() {