@samitouri / QOS-React / commits / 73bcdfbae5

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.