From 834d8915e192b7adcc98152592b3d02d8c21c178 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Wed, 3 Apr 2024 13:52:51 -0400 Subject: [PATCH] Experiment with using an object literal for Fiber creation Object literals should be faster at least on React Native with Hermes as the JS engine. It might also be interesting to confirm the old comments in this file from years ago are even still valid. Creating an object from a literal should be a simpler operation. It's a bit unfortunate that this introduces a bunch of copied code, but since we rearely update the fields on fibers, this seems like an okay tradeoff for a hot code path. An alternative would be some sort of macro system, but that doesn't seem worth the extra complexity. --- packages/react-reconciler/src/ReactFiber.js | 226 +++++++++++++++++- packages/shared/ReactFeatureFlags.js | 5 + .../ReactFeatureFlags.native-fb-dynamic.js | 1 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 8 files changed, 230 insertions(+), 7 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 7b1a6514ebd5f..3a15615e9b790 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -28,16 +28,17 @@ import { isHostSingletonType, } from './ReactFiberConfig'; import { - enableProfilerTimer, - enableScopeAPI, - enableLegacyHidden, - forceConcurrentByDefaultForTesting, allowConcurrentByDefault, - enableTransitionTracing, + disableLegacyMode, enableDebugTracing, enableDO_NOT_USE_disableStrictPassiveEffect, + enableLegacyHidden, + enableObjectFiber, + enableProfilerTimer, enableRenderableContext, - disableLegacyMode, + enableScopeAPI, + enableTransitionTracing, + forceConcurrentByDefaultForTesting, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot} from './ReactRootTags'; @@ -222,7 +223,7 @@ function FiberNode( // is faster. // 5) It should be easy to port this to a C struct and keep a C implementation // compatible. -function createFiber( +function createFiberImplClass( tag: WorkTag, pendingProps: mixed, key: null | string, @@ -232,6 +233,217 @@ function createFiber( return new FiberNode(tag, pendingProps, key, mode); } +function createFiberImplObjectDev( + tag: WorkTag, + pendingProps: mixed, + key: null | string, + mode: TypeOfMode, +): Fiber { + const fiber = { + // Instance + tag, + key, + elementType: null, + type: null, + stateNode: null, + + // Fiber + return: null, + child: null, + sibling: null, + index: 0, + + ref: null, + refCleanup: null, + + pendingProps, + memoizedProps: null, + updateQueue: null, + memoizedState: null, + dependencies: null, + + mode, + + // Effects + flags: NoFlags, + subtreeFlags: NoFlags, + deletions: null, + + lanes: NoLanes, + childLanes: NoLanes, + + alternate: null, + + // __DEV__ only + _debugInfo: null, + _debugOwner: null, + _debugNeedsRemount: false, + _debugHookTypes: null, + }; + if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { + Object.preventExtensions(fiber); + } + return fiber; +} +function createFiberImplObjectDevProfile( + tag: WorkTag, + pendingProps: mixed, + key: null | string, + mode: TypeOfMode, +): Fiber { + const fiber = { + // Instance + tag, + key, + elementType: null, + type: null, + stateNode: null, + + // Fiber + return: null, + child: null, + sibling: null, + index: 0, + + ref: null, + refCleanup: null, + + pendingProps, + memoizedProps: null, + updateQueue: null, + memoizedState: null, + dependencies: null, + + mode, + + // Effects + flags: NoFlags, + subtreeFlags: NoFlags, + deletions: null, + + lanes: NoLanes, + childLanes: NoLanes, + + alternate: null, + + // enableProfilerTimer only + actualDuration: 0, + actualStartTime: -1, + selfBaseDuration: 0, + treeBaseDuration: 0, + + // __DEV__ only + _debugInfo: null, + _debugOwner: null, + _debugNeedsRemount: false, + _debugHookTypes: null, + }; + if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { + Object.preventExtensions(fiber); + } + return fiber; +} +function createFiberImplObjectProd( + tag: WorkTag, + pendingProps: mixed, + key: null | string, + mode: TypeOfMode, +): Fiber { + return { + // Instance + tag, + key, + elementType: null, + type: null, + stateNode: null, + + // Fiber + return: null, + child: null, + sibling: null, + index: 0, + + ref: null, + refCleanup: null, + + pendingProps, + memoizedProps: null, + updateQueue: null, + memoizedState: null, + dependencies: null, + + mode, + + // Effects + flags: NoFlags, + subtreeFlags: NoFlags, + deletions: null, + + lanes: NoLanes, + childLanes: NoLanes, + + alternate: null, + }; +} +function createFiberImplObjectProdProfile( + tag: WorkTag, + pendingProps: mixed, + key: null | string, + mode: TypeOfMode, +): Fiber { + return { + // Instance + tag, + key, + elementType: null, + type: null, + stateNode: null, + + // Fiber + return: null, + child: null, + sibling: null, + index: 0, + + ref: null, + refCleanup: null, + + pendingProps, + memoizedProps: null, + updateQueue: null, + memoizedState: null, + dependencies: null, + + mode, + + // Effects + flags: NoFlags, + subtreeFlags: NoFlags, + deletions: null, + + lanes: NoLanes, + childLanes: NoLanes, + + alternate: null, + + // enableProfilerTimer only + actualDuration: 0, + actualStartTime: -1, + selfBaseDuration: 0, + treeBaseDuration: 0, + }; +} + +const createFiber = !enableObjectFiber + ? createFiberImplClass + : __DEV__ + ? enableProfilerTimer + ? createFiberImplObjectDevProfile + : createFiberImplObjectDev + : enableProfilerTimer + ? createFiberImplObjectProdProfile + : createFiberImplObjectProd; + function shouldConstruct(Component: Function) { const prototype = Component.prototype; return !!(prototype && prototype.isReactComponent); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index c527d069dcaa5..eee80fb5f48bd 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -83,6 +83,11 @@ export const enableTaint = __EXPERIMENTAL__; export const enablePostpone = __EXPERIMENTAL__; +/** + * Switches Fiber creation to a simple object instead of a constructor. + */ +export const enableObjectFiber = false; + export const enableTransitionTracing = false; // No known bugs, but needs performance testing diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 50c65b0038e16..77b2cc33ebe55 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -27,3 +27,4 @@ export const enableRenderableContext = __VARIANT__; export const enableUnifiedSyncLane = __VARIANT__; export const passChildrenWhenCloningPersistedNodes = __VARIANT__; export const useModernStrictMode = __VARIANT__; +export const enableObjectFiber = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 004c76d747569..9103e192d3aa1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -25,6 +25,7 @@ export const { enableComponentStackLocations, enableDeferRootSchedulingToMicrotask, enableInfiniteRenderLoopDetection, + enableObjectFiber, enableRenderableContext, enableUnifiedSyncLane, passChildrenWhenCloningPersistedNodes, diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 79f77bdc052ca..e72c76fe5e048 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -72,6 +72,7 @@ export const enableSchedulingProfiler = __PROFILE__; export const enableLegacyCache = false; export const enableFetchInstrumentation = false; export const enablePostpone = false; +export const enableObjectFiber = false; export const disableCommentsAsDOMContainers = true; export const disableInputAttributeSyncing = false; export const disableIEWorkarounds = true; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index cef2b788b73ce..f3e6eaa1721c9 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -90,6 +90,7 @@ export const disableLegacyContext = true; export const disableDOMTestUtils = true; export const enableRenderableContext = true; export const enableReactTestRendererWarning = true; +export const enableObjectFiber = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 3bfc66a0069ba..c73f90a8fb3f1 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -84,6 +84,7 @@ export const disableStringRefs = false; export const enableReactTestRendererWarning = false; export const disableLegacyMode = false; export const disableDOMTestUtils = false; +export const enableObjectFiber = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 3057542f39740..60916213a9e9b 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -114,6 +114,7 @@ export const disableStringRefs = false; export const disableLegacyMode = __EXPERIMENTAL__; export const disableDOMTestUtils = false; +export const enableObjectFiber = false; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType);