forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Flight] Log Server Component into Performance Track (facebook#31729)
<img width="966" alt="Screenshot 2024-12-10 at 10 49 19 PM" src="https://github.com/user-attachments/assets/27a21bdf-86b9-4203-893b-89523e698138"> This emits a tree view visualization of the timing information for each Server Component provided in the RSC payload. The unique thing about this visualization is that the end time of each Server Component spans the end of the last child. Now what is conceptually a blocking child is kind of undefined in RSC. E.g. if you're not using a Promise on the client, or if it is wrapped in Suspense, is it really blocking the parent? Here I reconstruct parent-child relationship by which chunks reference other chunks. A child can belong to more than one parent like when we dedupe the result of a Server Component. Then I wait until the whole RSC payload has streamed in, and then I traverse the tree collecting the end time from children as I go and emit the `performance.measure()` calls on the way up. There's more work for this visualization in follow ups but this is the basics. For example, since the Server Component time span includes async work it's possible for siblings to execute their span in parallel (Foo and Bar in the screenshot are parallel siblings). To deal with this we need to spawn parallel work into separate tracks. Each one can be deep due to large trees. This can makes this type of visualization unwieldy when you have a lot of parallelism. Therefore I also plan another flatter Timeline visualization in a follow up.
- Loading branch information
1 parent
ca58742
commit 6928bf2
Showing
3 changed files
with
190 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* 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. | ||
* | ||
* @flow | ||
*/ | ||
|
||
import type {ReactComponentInfo} from 'shared/ReactTypes'; | ||
|
||
import {enableProfilerTimer} from 'shared/ReactFeatureFlags'; | ||
|
||
const supportsUserTiming = | ||
enableProfilerTimer && | ||
typeof performance !== 'undefined' && | ||
// $FlowFixMe[method-unbinding] | ||
typeof performance.measure === 'function'; | ||
|
||
const COMPONENTS_TRACK = 'Server Components ⚛'; | ||
|
||
// Reused to avoid thrashing the GC. | ||
const reusableComponentDevToolDetails = { | ||
color: 'primary', | ||
track: COMPONENTS_TRACK, | ||
}; | ||
const reusableComponentOptions = { | ||
start: -0, | ||
end: -0, | ||
detail: { | ||
devtools: reusableComponentDevToolDetails, | ||
}, | ||
}; | ||
|
||
export function logComponentRender( | ||
componentInfo: ReactComponentInfo, | ||
startTime: number, | ||
endTime: number, | ||
childrenEndTime: number, | ||
): void { | ||
if (supportsUserTiming && childrenEndTime >= 0) { | ||
const name = componentInfo.name; | ||
const selfTime = endTime - startTime; | ||
reusableComponentDevToolDetails.color = | ||
selfTime < 0.5 | ||
? 'primary-light' | ||
: selfTime < 50 | ||
? 'primary' | ||
: selfTime < 500 | ||
? 'primary-dark' | ||
: 'error'; | ||
reusableComponentOptions.start = startTime < 0 ? 0 : startTime; | ||
reusableComponentOptions.end = childrenEndTime; | ||
performance.measure(name, reusableComponentOptions); | ||
} | ||
} |