-
Notifications
You must be signed in to change notification settings - Fork 48.8k
Profiler integration with interaction-tracking package #13253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae6e020
ee93597
70ed35f
91814d1
4c93074
c9b6c87
26dc2a8
235642b
1428c46
a173f36
05fa0e6
e61eb6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,11 +10,13 @@ | |
import type {Fiber} from './ReactFiber'; | ||
import type {ExpirationTime} from './ReactFiberExpirationTime'; | ||
import type {TimeoutHandle, NoTimeout} from './ReactFiberHostConfig'; | ||
import type {Interaction} from 'interaction-tracking/src/InteractionTracking'; | ||
|
||
import {noTimeout} from './ReactFiberHostConfig'; | ||
|
||
import {createHostRootFiber} from './ReactFiber'; | ||
import {NoWork} from './ReactFiberExpirationTime'; | ||
import {enableInteractionTracking} from 'shared/ReactFeatureFlags'; | ||
import {getThreadID} from 'interaction-tracking'; | ||
|
||
// TODO: This should be lifted into the renderer. | ||
export type Batch = { | ||
|
@@ -24,7 +26,9 @@ export type Batch = { | |
_next: Batch | null, | ||
}; | ||
|
||
export type FiberRoot = { | ||
export type PendingInteractionMap = Map<ExpirationTime, Set<Interaction>>; | ||
|
||
type BaseFiberRootProperties = {| | ||
// Any additional information from the host associated with this root. | ||
containerInfo: any, | ||
// Used only by persistent updates. | ||
|
@@ -73,6 +77,26 @@ export type FiberRoot = { | |
firstBatch: Batch | null, | ||
// Linked-list of roots | ||
nextScheduledRoot: FiberRoot | null, | ||
|}; | ||
|
||
// The following attributes are only used by interaction tracking builds. | ||
// They enable interactions to be associated with their async work, | ||
// And expose interaction metadata to the React DevTools Profiler plugin. | ||
// Note that these attributes are only defined when the enableInteractionTracking flag is enabled. | ||
type ProfilingOnlyFiberRootProperties = {| | ||
interactionThreadID: number, | ||
memoizedInteractions: Set<Interaction>, | ||
pendingInteractionMap: PendingInteractionMap, | ||
|}; | ||
|
||
// Exported FiberRoot type includes all properties, | ||
// To avoid requiring potentially error-prone :any casts throughout the project. | ||
// Profiling properties are only safe to access in profiling builds (when enableInteractionTracking is true). | ||
// The types are defined separately within this file to ensure they stay in sync. | ||
// (We don't have to use an inline :any cast when enableInteractionTracking is disabled.) | ||
export type FiberRoot = { | ||
...BaseFiberRootProperties, | ||
...ProfilingOnlyFiberRootProperties, | ||
}; | ||
|
||
export function createFiberRoot( | ||
|
@@ -83,30 +107,69 @@ export function createFiberRoot( | |
// Cyclic construction. This cheats the type system right now because | ||
// stateNode is any. | ||
const uninitializedFiber = createHostRootFiber(isAsync); | ||
const root = { | ||
current: uninitializedFiber, | ||
containerInfo: containerInfo, | ||
pendingChildren: null, | ||
|
||
earliestPendingTime: NoWork, | ||
latestPendingTime: NoWork, | ||
earliestSuspendedTime: NoWork, | ||
latestSuspendedTime: NoWork, | ||
latestPingedTime: NoWork, | ||
|
||
didError: false, | ||
|
||
pendingCommitExpirationTime: NoWork, | ||
finishedWork: null, | ||
timeoutHandle: noTimeout, | ||
context: null, | ||
pendingContext: null, | ||
hydrate, | ||
nextExpirationTimeToWorkOn: NoWork, | ||
expirationTime: NoWork, | ||
firstBatch: null, | ||
nextScheduledRoot: null, | ||
}; | ||
|
||
let root; | ||
if (enableInteractionTracking) { | ||
root = ({ | ||
current: uninitializedFiber, | ||
containerInfo: containerInfo, | ||
pendingChildren: null, | ||
|
||
earliestPendingTime: NoWork, | ||
latestPendingTime: NoWork, | ||
earliestSuspendedTime: NoWork, | ||
latestSuspendedTime: NoWork, | ||
latestPingedTime: NoWork, | ||
|
||
didError: false, | ||
|
||
pendingCommitExpirationTime: NoWork, | ||
finishedWork: null, | ||
timeoutHandle: noTimeout, | ||
context: null, | ||
pendingContext: null, | ||
hydrate, | ||
nextExpirationTimeToWorkOn: NoWork, | ||
expirationTime: NoWork, | ||
firstBatch: null, | ||
nextScheduledRoot: null, | ||
|
||
interactionThreadID: getThreadID(), | ||
memoizedInteractions: new Set(), | ||
pendingInteractionMap: new Map(), | ||
}: FiberRoot); | ||
} else { | ||
root = ({ | ||
current: uninitializedFiber, | ||
containerInfo: containerInfo, | ||
pendingChildren: null, | ||
|
||
earliestPendingTime: NoWork, | ||
latestPendingTime: NoWork, | ||
earliestSuspendedTime: NoWork, | ||
latestSuspendedTime: NoWork, | ||
latestPingedTime: NoWork, | ||
|
||
didError: false, | ||
|
||
pendingCommitExpirationTime: NoWork, | ||
finishedWork: null, | ||
timeoutHandle: noTimeout, | ||
context: null, | ||
pendingContext: null, | ||
hydrate, | ||
nextExpirationTimeToWorkOn: NoWork, | ||
expirationTime: NoWork, | ||
firstBatch: null, | ||
nextScheduledRoot: null, | ||
}: BaseFiberRootProperties); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bvaughn Can I ask that why we do the repetitive logic, i.e. only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do it for performance since this code is really "hot" (it runs a lot). Seb would be a much better person to answer this question but I'll take a stab at it. 😄 JavaScript VM makes property lookup faster for objects by creating a class in the native-code layer based on the "shape" of the object (e.g. what keys it has). This is called the "hidden class", and it's a runtime optimization. If the VM isn't sure about the object's shape, it uses a slower (hash table) method to lookup properties. When fields are added/deleted after an object is created, it breaks the VM's "hidden class" assumptions and de-opts to hash table lookup. Probably worth remembering that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. http://v8-io12.appspot.com/#29 Thank you Brian! I know the performance now. |
||
uninitializedFiber.stateNode = root; | ||
return root; | ||
|
||
// The reason for the way the Flow types are structured in this file, | ||
// Is to avoid needing :any casts everywhere interaction-tracking fields are used. | ||
// Unfortunately that requires an :any cast for non-interaction-tracking capable builds. | ||
// $FlowFixMe Remove this :any cast and replace it with something better. | ||
return ((root: any): FiberRoot); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try using an intersection type here? Or a spread type. Then the
any
below shouldn't be necessary.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This was the only type combo I could come up with that worked. If you can show me another syntax that Flow would support though, I'm happy to change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I added a $FlowFixMe comment here. I don't know of a better way to do this, but we can improve it with a follow up PR.