From 7063516a390fee95e7b42e00c4534c1eefd0b3d7 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Sat, 28 Feb 2026 15:04:12 -0800 Subject: [PATCH] Replace TinyMap with std::unordered_map in Differentiator (#55680) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: changelog: [internal] Replace TinyMap with std::unordered_map. There is no detectable regression in performance and code is simpler, let's remove TinyMap. ``` ┌────────────────────────────┬──────────────────┬───────────────────────────────────┬───────────────┐ │ Benchmark │ TinyMap (before) │ unordered_map + ctor size (after) │ Change │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 100 uncollapsable views │ 1,499,917 ns │ 1,343,333 ns │ -10.4% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1000 uncollapsable views │ 52,148,021 ns │ 44,131,792 ns │ -15.4% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 100 views large props │ 5,131,292 ns │ 4,529,937 ns │ -11.7% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1000 views large props │ 82,706,813 ns │ 84,334,167 ns │ +2.0% (noise) │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1500 views large props │ 61,470,542 ns │ 62,976,646 ns │ +2.5% (noise) │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=5, breadth=4) │ 32,672,583 ns │ 30,700,542 ns │ -6.0% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=7, breadth=3) │ 78,523,542 ns │ 74,121,729 ns │ -5.6% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=10, breadth=2) │ 48,328,562 ns │ 45,702,188 ns │ -5.4% │ └────────────────────────────┴──────────────────┴───────────────────────────────────┴───────────────┘ ``` Reviewed By: NickGerleman Differential Revision: D90346356 --- .../View/__tests__/View-benchmark-itest.js | 49 +++++ .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 32 +++- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../LayoutAnimationKeyFrameManager.cpp | 3 +- .../renderer/mounting/Differentiator.cpp | 59 +++--- .../renderer/mounting/internal/DiffMap.h | 170 ++++++++++++++++++ .../internal/sliceChildShadowNodeViewPairs.h | 6 +- .../ReactNativeFeatureFlags.config.js | 11 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 25 files changed, 387 insertions(+), 69 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/mounting/internal/DiffMap.h diff --git a/packages/react-native/Libraries/Components/View/__tests__/View-benchmark-itest.js b/packages/react-native/Libraries/Components/View/__tests__/View-benchmark-itest.js index 5d19b6c52a1e..10930caf2f98 100644 --- a/packages/react-native/Libraries/Components/View/__tests__/View-benchmark-itest.js +++ b/packages/react-native/Libraries/Components/View/__tests__/View-benchmark-itest.js @@ -17,6 +17,31 @@ import {View} from 'react-native'; let root; let testViews: React.MixedElement; +function createDeepViewHierarchy(depth: number, breadth: number): React.Node { + if (depth === 0) { + return ( + + ); + } + const children = []; + for (let i = 0; i < breadth; i++) { + children.push( + + {createDeepViewHierarchy(depth - 1, breadth)} + , + ); + } + return {children}; +} + function createViewsWithLargeAmountOfPropsAndStyles(count: number): React.Node { let views: React.Node = null; for (let i = 0; i < count; i++) { @@ -124,4 +149,28 @@ Fantom.unstable_benchmark root.destroy(); }, }), + ) + .test.each( + [ + [5, 4], + [7, 3], + [10, 2], + ], + ([depth, breadth]) => + `render deep view hierarchy (depth=${depth.toString()}, breadth=${breadth.toString()})`, + () => { + Fantom.runTask(() => root.render(testViews)); + }, + ([depth, breadth]) => ({ + beforeAll: () => { + // $FlowExpectedError[incompatible-type] + testViews = createDeepViewHierarchy(depth, breadth); + }, + beforeEach: () => { + root = Fantom.createRoot(); + }, + afterEach: () => { + root.destroy(); + }, + }), ); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 3e0e99e6af11..65d5f0c5328c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<477777b9a795b57f3bb3eaeb030738a9>> + * @generated SignedSource<<4e5ae706df013f43dd19f98cf905a138>> */ /** @@ -498,6 +498,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun useSharedAnimatedBackend(): Boolean = accessor.useSharedAnimatedBackend() + /** + * Use std::unordered_map instead of TinyMap in the Differentiator for improved lookup performance. + */ + @JvmStatic + public fun useUnorderedMapInDifferentiator(): Boolean = accessor.useUnorderedMapInDifferentiator() + /** * Use Trait::hidden on Android */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index bbd27b4d6433..ade8d499f7aa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6d7e2160c022ae4630f8d3a4b263621f>> */ /** @@ -98,6 +98,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNestedScrollViewAndroidCache: Boolean? = null private var useSharedAnimatedBackendCache: Boolean? = null + private var useUnorderedMapInDifferentiatorCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null private var useTurboModulesCache: Boolean? = null @@ -807,6 +808,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun useUnorderedMapInDifferentiator(): Boolean { + var cached = useUnorderedMapInDifferentiatorCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useUnorderedMapInDifferentiator() + useUnorderedMapInDifferentiatorCache = cached + } + return cached + } + override fun useTraitHiddenOnAndroid(): Boolean { var cached = useTraitHiddenOnAndroidCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 5aa0113e15e4..ec78931b8323 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -184,6 +184,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun useSharedAnimatedBackend(): Boolean + @DoNotStrip @JvmStatic public external fun useUnorderedMapInDifferentiator(): Boolean + @DoNotStrip @JvmStatic public external fun useTraitHiddenOnAndroid(): Boolean @DoNotStrip @JvmStatic public external fun useTurboModuleInterop(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 0950fb163232..e50a533e7e4e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<523e3c35d4bd1fc85f2a3bb26b8aad3f>> + * @generated SignedSource<> */ /** @@ -179,6 +179,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useSharedAnimatedBackend(): Boolean = false + override fun useUnorderedMapInDifferentiator(): Boolean = false + override fun useTraitHiddenOnAndroid(): Boolean = false override fun useTurboModuleInterop(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 656dbba20689..39c65d4c2fb0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -102,6 +102,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null private var useNestedScrollViewAndroidCache: Boolean? = null private var useSharedAnimatedBackendCache: Boolean? = null + private var useUnorderedMapInDifferentiatorCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null private var useTurboModulesCache: Boolean? = null @@ -889,6 +890,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun useUnorderedMapInDifferentiator(): Boolean { + var cached = useUnorderedMapInDifferentiatorCache + if (cached == null) { + cached = currentProvider.useUnorderedMapInDifferentiator() + accessedFeatureFlags.add("useUnorderedMapInDifferentiator") + useUnorderedMapInDifferentiatorCache = cached + } + return cached + } + override fun useTraitHiddenOnAndroid(): Boolean { var cached = useTraitHiddenOnAndroidCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index bb2f3ac62adf..da32f0a57545 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<07b19c2cdde81032b3181a658e528b18>> */ /** @@ -179,6 +179,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun useSharedAnimatedBackend(): Boolean + @DoNotStrip public fun useUnorderedMapInDifferentiator(): Boolean + @DoNotStrip public fun useTraitHiddenOnAndroid(): Boolean @DoNotStrip public fun useTurboModuleInterop(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 15c606bb8072..53e070d28e41 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<45063df01d7ce8726b4a7d901f1b3341>> + * @generated SignedSource<> */ /** @@ -507,6 +507,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool useUnorderedMapInDifferentiator() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useUnorderedMapInDifferentiator"); + return method(javaProvider_); + } + bool useTraitHiddenOnAndroid() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useTraitHiddenOnAndroid"); @@ -937,6 +943,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useSharedAnimatedBackend( return ReactNativeFeatureFlags::useSharedAnimatedBackend(); } +bool JReactNativeFeatureFlagsCxxInterop::useUnorderedMapInDifferentiator( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useUnorderedMapInDifferentiator(); +} + bool JReactNativeFeatureFlagsCxxInterop::useTraitHiddenOnAndroid( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useTraitHiddenOnAndroid(); @@ -1232,6 +1243,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "useSharedAnimatedBackend", JReactNativeFeatureFlagsCxxInterop::useSharedAnimatedBackend), + makeNativeMethod( + "useUnorderedMapInDifferentiator", + JReactNativeFeatureFlagsCxxInterop::useUnorderedMapInDifferentiator), makeNativeMethod( "useTraitHiddenOnAndroid", JReactNativeFeatureFlagsCxxInterop::useTraitHiddenOnAndroid), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index f5cd383bdeeb..6e21b3fb93a4 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5ac93ed057017f8d1a388b8029614f18>> + * @generated SignedSource<<519ec7b20ed2b4feaf10d1448c68c255>> */ /** @@ -264,6 +264,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool useSharedAnimatedBackend( facebook::jni::alias_ref); + static bool useUnorderedMapInDifferentiator( + facebook::jni::alias_ref); + static bool useTraitHiddenOnAndroid( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 37f971aad1bb..25468778268c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9d81f74c5926706ee353813e594575e8>> + * @generated SignedSource<<168ae9f867afd3e316d3ba4925ffa07e>> */ /** @@ -338,6 +338,10 @@ bool ReactNativeFeatureFlags::useSharedAnimatedBackend() { return getAccessor().useSharedAnimatedBackend(); } +bool ReactNativeFeatureFlags::useUnorderedMapInDifferentiator() { + return getAccessor().useUnorderedMapInDifferentiator(); +} + bool ReactNativeFeatureFlags::useTraitHiddenOnAndroid() { return getAccessor().useTraitHiddenOnAndroid(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 8a1ca2766b22..d3d62c0bd488 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<84e2800073ffab2313a4e27897c0c246>> + * @generated SignedSource<<88bd716e389c2929f48a4e400170f7fa>> */ /** @@ -429,6 +429,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool useSharedAnimatedBackend(); + /** + * Use std::unordered_map instead of TinyMap in the Differentiator for improved lookup performance. + */ + RN_EXPORT static bool useUnorderedMapInDifferentiator(); + /** * Use Trait::hidden on Android */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index aafd52dd5889..f0514fabe5a4 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8f9f3ced66040f8275073e5084765356>> + * @generated SignedSource<<5c22b9b03b250ee9b535182780651784>> */ /** @@ -1433,6 +1433,24 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() { + auto flagValue = useUnorderedMapInDifferentiator_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(78, "useUnorderedMapInDifferentiator"); + + flagValue = currentProvider_->useUnorderedMapInDifferentiator(); + useUnorderedMapInDifferentiator_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { auto flagValue = useTraitHiddenOnAndroid_.load(); @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(79, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useTurboModuleInterop"); + markFlagAsAccessed(80, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useTurboModules"); + markFlagAsAccessed(81, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1496,7 +1514,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "viewCullingOutsetRatio"); + markFlagAsAccessed(82, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "viewTransitionEnabled"); + markFlagAsAccessed(83, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1532,7 +1550,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "virtualViewPrerenderRatio"); + markFlagAsAccessed(84, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 28f655e04c0e..c83ec768b872 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6ba661fd9ce6aeff6cc9269848230618>> + * @generated SignedSource<<0c58dd7fbd5561ec62dd5c25d1351336>> */ /** @@ -110,6 +110,7 @@ class ReactNativeFeatureFlagsAccessor { bool useNativeViewConfigsInBridgelessMode(); bool useNestedScrollViewAndroid(); bool useSharedAnimatedBackend(); + bool useUnorderedMapInDifferentiator(); bool useTraitHiddenOnAndroid(); bool useTurboModuleInterop(); bool useTurboModules(); @@ -127,7 +128,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 84> accessedFeatureFlags_; + std::array, 85> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -207,6 +208,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> useNativeViewConfigsInBridgelessMode_; std::atomic> useNestedScrollViewAndroid_; std::atomic> useSharedAnimatedBackend_; + std::atomic> useUnorderedMapInDifferentiator_; std::atomic> useTraitHiddenOnAndroid_; std::atomic> useTurboModuleInterop_; std::atomic> useTurboModules_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index e17c94e91f53..a4348f0d5955 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<861e4a47ad9b2aa4ac054059082724a0>> + * @generated SignedSource<<874dda2a17484941b243bc90e08d1192>> */ /** @@ -339,6 +339,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool useUnorderedMapInDifferentiator() override { + return false; + } + bool useTraitHiddenOnAndroid() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 56c0e084c0f9..74515fae6178 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<774ffd15e1fd9a79bb7b3f6c4719ba23>> + * @generated SignedSource<<85e793611d955290f8263ecb23092f46>> */ /** @@ -747,6 +747,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::useSharedAnimatedBackend(); } + bool useUnorderedMapInDifferentiator() override { + auto value = values_["useUnorderedMapInDifferentiator"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::useUnorderedMapInDifferentiator(); + } + bool useTraitHiddenOnAndroid() override { auto value = values_["useTraitHiddenOnAndroid"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index cadf99532f4c..c4f392855720 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<0526ff6c17eb9a0b36e580e4d643aa23>> */ /** @@ -103,6 +103,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool useNativeViewConfigsInBridgelessMode() = 0; virtual bool useNestedScrollViewAndroid() = 0; virtual bool useSharedAnimatedBackend() = 0; + virtual bool useUnorderedMapInDifferentiator() = 0; virtual bool useTraitHiddenOnAndroid() = 0; virtual bool useTurboModuleInterop() = 0; virtual bool useTurboModules() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 2d845642de15..3a28d498fcd8 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0356babbe4e65071e6cc56a06e68b944>> + * @generated SignedSource<<4f0b6e01a4df551f79a46e2fc6bd269d>> */ /** @@ -434,6 +434,11 @@ bool NativeReactNativeFeatureFlags::useSharedAnimatedBackend( return ReactNativeFeatureFlags::useSharedAnimatedBackend(); } +bool NativeReactNativeFeatureFlags::useUnorderedMapInDifferentiator( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useUnorderedMapInDifferentiator(); +} + bool NativeReactNativeFeatureFlags::useTraitHiddenOnAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useTraitHiddenOnAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index bfb867589ab0..f1842ae9f09d 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2b82eb6d91d0b4437aa3b25f0488fe3e>> + * @generated SignedSource<> */ /** @@ -192,6 +192,8 @@ class NativeReactNativeFeatureFlags bool useSharedAnimatedBackend(jsi::Runtime& runtime); + bool useUnorderedMapInDifferentiator(jsi::Runtime& runtime); + bool useTraitHiddenOnAndroid(jsi::Runtime& runtime); bool useTurboModuleInterop(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 8abb96cfcb97..320bbfe0bcfc 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -256,8 +256,7 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Catch delete+create (reparenting) (this should be optimized away at // the diffing level eventually?) // TODO: to prevent this step we could tag Remove/Insert mutations as - // being moves on the Differ level, since we know that there? We could use - // TinyMap here, but it's not exposed by Differentiator (yet). + // being moves on the Differ level, since we know that there? std::unordered_set insertedTags; std::unordered_set deletedTags; std::unordered_set diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index dff263a34981..d3c7c92376fd 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -9,11 +9,10 @@ #include #include -#include #include #include "internal/CullingContext.h" +#include "internal/DiffMap.h" #include "internal/ShadowViewNodePair.h" -#include "internal/TinyMap.h" #include "internal/sliceChildShadowNodeViewPairs.h" #include "ShadowView.h" @@ -58,7 +57,7 @@ static std::ostream& operator<<( #ifdef DEBUG_LOGS_DIFFER template -static std::ostream& operator<<(std::ostream& out, TinyMap& map) { +static std::ostream& operator<<(std::ostream& out, DiffMap& map) { auto it = map.begin(); if (it != map.end()) { out << *it->second; @@ -143,7 +142,7 @@ struct OrderedMutationInstructionContainer { static void updateMatchedPairSubtrees( ViewNodePairScope& scope, OrderedMutationInstructionContainer& mutationContainer, - TinyMap& newRemainingPairs, + DiffMap& newRemainingPairs, std::vector& oldChildPairs, Tag parentTag, const ShadowViewNodePair& oldPair, @@ -164,11 +163,11 @@ static void calculateShadowViewMutationsFlattener( ReparentMode reparentMode, OrderedMutationInstructionContainer& mutationContainer, Tag parentTag, - TinyMap& unvisitedOtherNodes, + DiffMap& unvisitedOtherNodes, const ShadowViewNodePair& node, Tag parentTagForUpdate, - TinyMap* parentSubVisitedOtherNewNodes, - TinyMap* parentSubVisitedOtherOldNodes, + DiffMap* parentSubVisitedOtherNewNodes, + DiffMap* parentSubVisitedOtherOldNodes, const CullingContext& cullingContextForUnvisitedOtherNodes, const CullingContext& cullingContext); @@ -183,7 +182,7 @@ static void calculateShadowViewMutationsFlattener( static void updateMatchedPairSubtrees( ViewNodePairScope& scope, OrderedMutationInstructionContainer& mutationContainer, - TinyMap& newRemainingPairs, + DiffMap& newRemainingPairs, std::vector& oldChildPairs, Tag parentTag, const ShadowViewNodePair& oldPair, @@ -232,7 +231,6 @@ static void updateMatchedPairSubtrees( // Unflattening else { // Construct unvisited nodes map - auto unvisitedOldChildPairs = TinyMap{}; // We don't know where all the children of oldChildPair are // within oldChildPairs, but we know that they're in the same // relative order. The reason for this is because of flattening @@ -240,6 +238,8 @@ static void updateMatchedPairSubtrees( // interwoven with children from other nodes, etc. auto oldFlattenedNodes = sliceChildShadowNodeViewPairsFromViewNodePair( oldPair, scope, true, oldCullingContextCopy); + auto unvisitedOldChildPairs = + DiffMap(oldFlattenedNodes.size()); for (size_t i = 0, j = 0; i < oldChildPairs.size() && j < oldFlattenedNodes.size(); i++) { @@ -422,11 +422,11 @@ static void calculateShadowViewMutationsFlattener( ReparentMode reparentMode, OrderedMutationInstructionContainer& mutationContainer, Tag parentTag, - TinyMap& unvisitedOtherNodes, + DiffMap& unvisitedOtherNodes, const ShadowViewNodePair& node, Tag parentTagForUpdate, - TinyMap* parentSubVisitedOtherNewNodes, - TinyMap* parentSubVisitedOtherOldNodes, + DiffMap* parentSubVisitedOtherNewNodes, + DiffMap* parentSubVisitedOtherOldNodes, const CullingContext& cullingContextForUnvisitedOtherNodes, const CullingContext& cullingContext) { // Step 1: iterate through entire tree @@ -445,8 +445,8 @@ static void calculateShadowViewMutationsFlattener( // Views in other tree that are visited by sub-flattening or // sub-unflattening - TinyMap subVisitedOtherNewNodes{}; - TinyMap subVisitedOtherOldNodes{}; + DiffMap subVisitedOtherNewNodes{}; + DiffMap subVisitedOtherOldNodes{}; auto subVisitedNewMap = (parentSubVisitedOtherNewNodes != nullptr ? parentSubVisitedOtherNewNodes : &subVisitedOtherNewNodes); @@ -456,7 +456,7 @@ static void calculateShadowViewMutationsFlattener( // Candidates for full tree creation or deletion at the end of this function auto deletionCreationCandidatePairs = - TinyMap{}; + DiffMap(treeChildren.size()); for (size_t index = 0; index < treeChildren.size() && index < treeChildren.size(); @@ -471,7 +471,7 @@ static void calculateShadowViewMutationsFlattener( : subVisitedNewMap->end()); auto subVisitedOtherOldIt = (unvisitedIt == unvisitedOtherNodes.end() && - (subVisitedNewMap->end() != nullptr) + subVisitedOtherNewIt == subVisitedNewMap->end() ? subVisitedOldMap->find(treeChildPair.shadowView.tag) : subVisitedOldMap->end()); @@ -697,7 +697,7 @@ static void calculateShadowViewMutationsFlattener( : adjustedOldCullingContext); // Construct unvisited nodes map auto unvisitedRecursiveChildPairs = - TinyMap{}; + DiffMap(flattenedNodes.size()); for (auto& flattenedNode : flattenedNodes) { auto& newChild = *flattenedNode; @@ -756,9 +756,6 @@ static void calculateShadowViewMutationsFlattener( // loop of this function. for (auto& unvisitedRecursiveChildPair : unvisitedRecursiveChildPairs) { - if (unvisitedRecursiveChildPair.first == 0) { - continue; - } auto& oldFlattenedNode = *unvisitedRecursiveChildPair.second; // Node unvisited - mark the entire subtree for deletion @@ -820,9 +817,6 @@ static void calculateShadowViewMutationsFlattener( // subtrees if they were never visited during the execution of the above // loop and recursions. for (auto& deletionCreationCandidatePair : deletionCreationCandidatePairs) { - if (deletionCreationCandidatePair.first == 0) { - continue; - } auto& treeChildPair = *deletionCreationCandidatePair.second; // If node was visited during a flattening/unflattening recursion, @@ -1042,9 +1036,10 @@ static void calculateShadowViewMutations( } } else { // Collect map of tags in the new list - auto newRemainingPairs = TinyMap{}; - auto newInsertedPairs = TinyMap{}; - auto deletionCandidatePairs = TinyMap{}; + auto remainingCount = newChildPairs.size() - index; + auto newRemainingPairs = DiffMap(remainingCount); + auto newInsertedPairs = DiffMap(remainingCount); + auto deletionCandidatePairs = DiffMap{}; for (; index < newChildPairs.size(); index++) { auto& newChildPair = *newChildPairs[index]; newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair}); @@ -1243,10 +1238,6 @@ static void calculateShadowViewMutations( // list to make sure that a node was not reparented into an unflattened // node that occurs *after* it in the hierarchy, due to zIndex ordering. for (auto& deletionCandidatePair : deletionCandidatePairs) { - if (deletionCandidatePair.first == 0) { - continue; - } - const auto& oldChildPair = *deletionCandidatePair.second; DEBUG_LOGS({ @@ -1285,14 +1276,6 @@ static void calculateShadowViewMutations( // Final step: generate Create instructions for entirely new // subtrees/nodes that are not the result of flattening or unflattening. for (auto& newInsertedPair : newInsertedPairs) { - // Erased elements of a TinyMap will have a Tag/key of 0 - skip those - // These *should* be removed by the map; there are currently no KNOWN - // cases where TinyMap will do the wrong thing, but there are not yet - // any unit tests explicitly for TinyMap, so this is safer for now. - if (newInsertedPair.first == 0) { - continue; - } - const auto& newChildPair = *newInsertedPair.second; DEBUG_LOGS({ diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/DiffMap.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/DiffMap.h new file mode 100644 index 000000000000..14e05f685f9e --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/DiffMap.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include "TinyMap.h" + +namespace facebook::react { + +/** + * A facade that delegates to either TinyMap or std::unordered_map based on + * the useUnorderedMapInDifferentiator feature flag. This allows safe rollout + * of the unordered_map change in the Differentiator. + * + * Exposes a subset of the std::unordered_map interface used by the + * Differentiator: find, insert, erase, begin, end, and range-for. + * + * The Iterator returns const references because it uses a cached copy + * internally (the two backends have different pair types). The Differentiator + * only reads through iterators, never writes. + */ +template +class DiffMap final { + public: + using Pair = std::pair; + + class Iterator { + public: + const Pair &operator*() const + { + syncCached(); + return cached_; + } + + const Pair *operator->() const + { + syncCached(); + return &cached_; + } + + Iterator &operator++() + { + if (useUnorderedMap_) { + ++umIt_; + } else { + ++tinyIt_; + } + dirty_ = true; + return *this; + } + + bool operator==(const Iterator &other) const + { + if (useUnorderedMap_) { + return umIt_ == other.umIt_; + } + return tinyIt_ == other.tinyIt_; + } + + bool operator!=(const Iterator &other) const + { + return !(*this == other); + } + + private: + friend class DiffMap; + + void syncCached() const + { + if (!dirty_) { + return; + } + if (useUnorderedMap_) { + cached_ = {umIt_->first, umIt_->second}; + } else { + cached_ = {tinyIt_->first, tinyIt_->second}; + } + dirty_ = false; + } + + bool useUnorderedMap_{false}; + typename TinyMap::Iterator tinyIt_{nullptr}; + typename std::unordered_map::iterator umIt_{}; + mutable Pair cached_{}; + mutable bool dirty_{true}; + }; + + DiffMap() : useUnorderedMap_(ReactNativeFeatureFlags::useUnorderedMapInDifferentiator()) + { + if (useUnorderedMap_) { + unorderedMap_.emplace(); + } + } + + explicit DiffMap(size_t sizeHint) : useUnorderedMap_(ReactNativeFeatureFlags::useUnorderedMapInDifferentiator()) + { + if (useUnorderedMap_) { + unorderedMap_.emplace(); + unorderedMap_->reserve(sizeHint); + } + } + + Iterator begin() + { + Iterator it; + it.useUnorderedMap_ = useUnorderedMap_; + if (useUnorderedMap_) { + it.umIt_ = unorderedMap_->begin(); + } else { + it.tinyIt_ = tinyMap_.begin(); + } + return it; + } + + Iterator end() + { + Iterator it; + it.useUnorderedMap_ = useUnorderedMap_; + if (useUnorderedMap_) { + it.umIt_ = unorderedMap_->end(); + } else { + it.tinyIt_ = tinyMap_.end(); + } + return it; + } + + Iterator find(KeyT key) + { + Iterator it; + it.useUnorderedMap_ = useUnorderedMap_; + if (useUnorderedMap_) { + it.umIt_ = unorderedMap_->find(key); + } else { + it.tinyIt_ = tinyMap_.find(key); + } + return it; + } + + void insert(Pair pair) + { + if (useUnorderedMap_) { + unorderedMap_->insert(std::move(pair)); + } else { + tinyMap_.insert(std::move(pair)); + } + } + + void erase(Iterator it) + { + if (useUnorderedMap_) { + unorderedMap_->erase(it.umIt_); + } else { + tinyMap_.erase(it.tinyIt_); + } + } + + private: + bool useUnorderedMap_; + TinyMap tinyMap_; + std::optional> unorderedMap_; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h index 869da1d5de15..b85b1e13be46 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h @@ -18,10 +18,10 @@ struct ShadowViewNodePair; /** * During differ, we need to keep some `ShadowViewNodePair`s in memory. * Some `ShadowViewNodePair`s are referenced from std::vectors returned - * by `sliceChildShadowNodeViewPairs`; some are referenced in TinyMaps + * by `sliceChildShadowNodeViewPairs`; some are referenced in maps * for view (un)flattening especially; and it is not always clear which - * std::vectors will outlive which TinyMaps, and vice-versa, so it doesn't - * make sense for the std::vector or TinyMap to own any `ShadowViewNodePair`s. + * std::vectors will outlive which maps, and vice-versa, so it doesn't + * make sense for the std::vector or map to own any `ShadowViewNodePair`s. * * Thus, we introduce the concept of a scope. * diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index e8e429ba609c..5f82807e57d8 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -880,6 +880,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + useUnorderedMapInDifferentiator: { + defaultValue: false, + metadata: { + dateAdded: '2026-02-26', + description: + 'Use std::unordered_map instead of TinyMap in the Differentiator for improved lookup performance.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, useTraitHiddenOnAndroid: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 7939dec2f939..91958d900741 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -125,6 +125,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ useNativeViewConfigsInBridgelessMode: Getter, useNestedScrollViewAndroid: Getter, useSharedAnimatedBackend: Getter, + useUnorderedMapInDifferentiator: Getter, useTraitHiddenOnAndroid: Getter, useTurboModuleInterop: Getter, useTurboModules: Getter, @@ -509,6 +510,10 @@ export const useNestedScrollViewAndroid: Getter = createNativeFlagGette * Use shared animation backend in C++ Animated */ export const useSharedAnimatedBackend: Getter = createNativeFlagGetter('useSharedAnimatedBackend', false); +/** + * Use std::unordered_map instead of TinyMap in the Differentiator for improved lookup performance. + */ +export const useUnorderedMapInDifferentiator: Getter = createNativeFlagGetter('useUnorderedMapInDifferentiator', false); /** * Use Trait::hidden on Android */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 128f27b7f32e..06915d27c39f 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3495a8cf9d3d820e37d082b3d62e2b91>> + * @generated SignedSource<<907f8a9efe341ffb9cc8b81cb4e103b9>> * @flow strict * @noformat */ @@ -103,6 +103,7 @@ export interface Spec extends TurboModule { +useNativeViewConfigsInBridgelessMode?: () => boolean; +useNestedScrollViewAndroid?: () => boolean; +useSharedAnimatedBackend?: () => boolean; + +useUnorderedMapInDifferentiator?: () => boolean; +useTraitHiddenOnAndroid?: () => boolean; +useTurboModuleInterop?: () => boolean; +useTurboModules?: () => boolean;