Introduce a faster version of the addProperties function (#28969)
## Summary This PR introduces a faster version of the `addProperties` function. This new function is basically the `diffProperties` with `prevProps` set to `null`, propagated constants, and all the unreachable code paths collapsed. ## How did you test this change? I've tested this change with [the benchmark app](https://github.com/react-native-community/RNNewArchitectureApp/tree/new-architecture-benchmarks) and got ~4.4% improvement in the view creation time.
Dmytro Rykun committed
May 2, 2024 at 17:10 UTC
73bcdfbae57545aa8f88ecdf67426275610b5573
10 files changed
+78
-2
packages/react-native-renderer/src/ReactNativeAttributePayload.js
+68
-2
@@ -15,6 +15,7 @@ import {
15
import isArray from 'shared/isArray';
16
17
import {enableEarlyReturnForPropDiffing} from 'shared/ReactFeatureFlags';
18
+import {enableAddPropertiesFastPath} from 'shared/ReactFeatureFlags';
19
20
import type {AttributeConfiguration} from './ReactNativeTypes';
21
@@ -444,6 +445,68 @@ function diffProperties(
445
return updatePayload;
446
}
447
448
+function fastAddProperties(
449
+ updatePayload: null | Object,
450
+ nextProps: Object,
451
+ validAttributes: AttributeConfiguration,
452
+): null | Object {
453
+ let attributeConfig;
454
+ let nextProp;
455
+
456
+ for (const propKey in nextProps) {
457
+ nextProp = nextProps[propKey];
458
+
459
+ if (nextProp === undefined) {
460
+ continue;
461
+ }
462
+
463
+ attributeConfig = validAttributes[propKey];
464
+
465
+ if (attributeConfig === undefined) {
466
+ continue;
467
+ }
468
+
469
+ if (typeof nextProp === 'function') {
470
+ nextProp = (true: any);
471
+ }
472
+
473
+ if (typeof attributeConfig !== 'object') {
474
+ if (!updatePayload) {
475
+ updatePayload = ({}: {[string]: $FlowFixMe});
476
+ }
477
+ updatePayload[propKey] = nextProp;
478
+ continue;
479
+ }
480
+
481
+ if (typeof attributeConfig.process === 'function') {
482
+ if (!updatePayload) {
483
+ updatePayload = ({}: {[string]: $FlowFixMe});
484
+ }
485
+ updatePayload[propKey] = attributeConfig.process(nextProp);
486
+ continue;
487
+ }
488
+
489
+ if (isArray(nextProp)) {
490
+ for (let i = 0; i < nextProp.length; i++) {
491
+ updatePayload = fastAddProperties(
492
+ updatePayload,
493
+ nextProp[i],
494
+ ((attributeConfig: any): AttributeConfiguration),
495
+ );
496
+ }
497
+ continue;
498
+ }
499
+
500
+ updatePayload = fastAddProperties(
501
+ updatePayload,
502
+ nextProp,
503
+ ((attributeConfig: any): AttributeConfiguration),
504
+ );
505
+ }
506
+
507
+ return updatePayload;
508
+}
509
+
510
/**
511
* addProperties adds all the valid props to the payload after being processed.
512
*/
@@ -452,8 +515,11 @@ function addProperties(
515
props: Object,
516
validAttributes: AttributeConfiguration,
517
): null | Object {
455
- // TODO: Fast path
456
- return diffProperties(updatePayload, emptyObject, props, validAttributes);
518
+ if (enableAddPropertiesFastPath) {
519
+ return fastAddProperties(updatePayload, props, validAttributes);
520
+ } else {
521
+ return diffProperties(updatePayload, emptyObject, props, validAttributes);
522
+ }
523
}
524
525
/**
packages/shared/ReactFeatureFlags.js
+2
@@ -123,6 +123,8 @@ export const enableServerComponentLogs = __EXPERIMENTAL__;
123
124
export const enableEarlyReturnForPropDiffing = false;
125
126
+export const enableAddPropertiesFastPath = false;
127
+
128
/**
129
* Enables an expiration time for retry lanes to avoid starvation.
130
*/
packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js
+1
@@ -29,3 +29,4 @@ export const enableUnifiedSyncLane = __VARIANT__;
29
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
30
export const useModernStrictMode = __VARIANT__;
31
export const disableDefaultPropsExceptForClasses = __VARIANT__;
32
+export const enableAddPropertiesFastPath = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -31,6 +31,7 @@ export const {
31
passChildrenWhenCloningPersistedNodes,
32
useModernStrictMode,
33
disableDefaultPropsExceptForClasses,
34
+ enableAddPropertiesFastPath,
35
} = dynamicFlags;
36
37
// The rest of the flags are static for better dead code elimination.
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -103,6 +103,7 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
103
export const passChildrenWhenCloningPersistedNodes = false;
104
export const enableEarlyReturnForPropDiffing = false;
105
export const enableAsyncIterableChildren = false;
106
+export const enableAddPropertiesFastPath = false;
107
108
export const renameElementSymbol = true;
109
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -78,6 +78,7 @@ export const enableServerComponentKeys = true;
78
export const enableServerComponentLogs = true;
79
export const enableInfiniteRenderLoopDetection = false;
80
export const enableEarlyReturnForPropDiffing = false;
81
+export const enableAddPropertiesFastPath = false;
82
83
export const renameElementSymbol = true;
84
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
+1
@@ -89,6 +89,7 @@ export const disableDOMTestUtils = false;
89
90
export const disableDefaultPropsExceptForClasses = false;
91
export const enableEarlyReturnForPropDiffing = false;
92
+export const enableAddPropertiesFastPath = false;
93
94
export const renameElementSymbol = false;
95
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -89,6 +89,7 @@ export const disableDOMTestUtils = false;
89
90
export const disableDefaultPropsExceptForClasses = false;
91
export const enableEarlyReturnForPropDiffing = false;
92
+export const enableAddPropertiesFastPath = false;
93
94
export const renameElementSymbol = false;
95
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -31,6 +31,7 @@ export const enableNoCloningMemoCache = __VARIANT__;
31
export const retryLaneExpirationMs = 5000;
32
export const syncLaneExpirationMs = 250;
33
export const transitionLaneExpirationMs = 5000;
34
+export const enableAddPropertiesFastPath = __VARIANT__;
35
36
// Enable this flag to help with concurrent mode debugging.
37
// It logs information to the console about React scheduling, rendering, and commit phases.
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -35,6 +35,7 @@ export const {
35
favorSafetyOverHydrationPerf,
36
disableDefaultPropsExceptForClasses,
37
enableNoCloningMemoCache,
38
+ enableAddPropertiesFastPath,
39
} = dynamicFeatureFlags;
40
41
// On WWW, __EXPERIMENTAL__ is used for a new modern build.