Skip to content

Commit f0799b3

Browse files
committed
Log yielded time in the Component Track
1 parent 3b886cf commit f0799b3

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

packages/react-reconciler/src/ReactFiberPerformanceTrack.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,54 @@ export function logComponentEffect(
114114
}
115115
}
116116

117+
export function logYieldTime(startTime: number, endTime: number): void {
118+
if (supportsUserTiming) {
119+
const yieldDuration = endTime - startTime;
120+
if (yieldDuration < 1) {
121+
// Skip sub-millisecond yields. This happens all the time and is not interesting.
122+
return;
123+
}
124+
// Being blocked on CPU is potentially bad so we color it by how long it took.
125+
reusableComponentDevToolDetails.color =
126+
yieldDuration < 5
127+
? 'primary-light'
128+
: yieldDuration < 10
129+
? 'primary'
130+
: yieldDuration < 100
131+
? 'primary-dark'
132+
: 'error';
133+
reusableComponentOptions.start = startTime;
134+
reusableComponentOptions.end = endTime;
135+
performance.measure('Blocked', reusableComponentOptions);
136+
}
137+
}
138+
139+
export function logSuspendedYieldTime(
140+
startTime: number,
141+
endTime: number,
142+
suspendedFiber: Fiber,
143+
): void {
144+
if (supportsUserTiming) {
145+
reusableComponentDevToolDetails.color = 'primary-light';
146+
reusableComponentOptions.start = startTime;
147+
reusableComponentOptions.end = endTime;
148+
performance.measure('Suspended', reusableComponentOptions);
149+
}
150+
}
151+
152+
export function logActionYieldTime(
153+
startTime: number,
154+
endTime: number,
155+
suspendedFiber: Fiber,
156+
): void {
157+
if (supportsUserTiming) {
158+
reusableComponentDevToolDetails.color = 'primary-light';
159+
reusableComponentOptions.start = startTime;
160+
reusableComponentOptions.end = endTime;
161+
performance.measure('Action', reusableComponentOptions);
162+
}
163+
}
164+
117165
export function logBlockingStart(
118166
updateTime: number,
119167
eventTime: number,

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ import {
7979
logCommitPhase,
8080
logPaintYieldPhase,
8181
logPassiveCommitPhase,
82+
logYieldTime,
83+
logActionYieldTime,
84+
logSuspendedYieldTime,
8285
} from './ReactFiberPerformanceTrack';
8386

8487
import {
@@ -264,6 +267,9 @@ import {
264267
stopProfilerTimerIfRunningAndRecordIncompleteDuration,
265268
markUpdateAsRepeat,
266269
trackSuspendedTime,
270+
startYieldTimer,
271+
yieldStartTime,
272+
yieldReason,
267273
} from './ReactProfilerTimer';
268274
import {setCurrentTrackFromLanes} from './ReactFiberPerformanceTrack';
269275

@@ -351,7 +357,7 @@ let workInProgress: Fiber | null = null;
351357
// The lanes we're rendering
352358
let workInProgressRootRenderLanes: Lanes = NoLanes;
353359

354-
opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
360+
export opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
355361
const NotSuspended: SuspendedReason = 0;
356362
const SuspendedOnError: SuspendedReason = 1;
357363
const SuspendedOnData: SuspendedReason = 2;
@@ -915,6 +921,24 @@ export function performWorkOnRoot(
915921
throw new Error('Should not already be working.');
916922
}
917923

924+
if (enableProfilerTimer && enableComponentPerformanceTrack) {
925+
if (workInProgressRootRenderLanes !== NoLanes && workInProgress !== null) {
926+
const yieldedFiber = workInProgress;
927+
// We've returned from yielding to the event loop. Let's log the time it took.
928+
const yieldEndTime = now();
929+
switch (yieldReason) {
930+
case SuspendedOnData:
931+
logSuspendedYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);
932+
break;
933+
case SuspendedOnAction:
934+
logActionYieldTime(yieldStartTime, yieldEndTime, yieldedFiber);
935+
break;
936+
default:
937+
logYieldTime(yieldStartTime, yieldEndTime);
938+
}
939+
}
940+
}
941+
918942
// We disable time-slicing in some cases: if the work has been CPU-bound
919943
// for too long ("expired" work, to prevent starvation), or we're in
920944
// sync-updates-by-default mode.
@@ -956,6 +980,12 @@ export function performWorkOnRoot(
956980
const didAttemptEntireTree = false;
957981
markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
958982
}
983+
if (enableProfilerTimer && enableComponentPerformanceTrack) {
984+
// We're about to yield. Let's keep track of how long we yield to the event loop.
985+
// We also stash the suspended reason at the time we yielded since it might have
986+
// changed when we resume such as when it gets pinged.
987+
startYieldTimer(workInProgressSuspendedReason);
988+
}
959989
break;
960990
} else {
961991
let renderEndTime = 0;

packages/react-reconciler/src/ReactProfilerTimer.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import type {Fiber} from './ReactInternalTypes';
1111

12+
import type {SuspendedReason} from './ReactFiberWorkLoop';
13+
1214
import type {Lane, Lanes} from './ReactFiberLane';
1315
import {
1416
isTransitionLane,
@@ -58,6 +60,17 @@ export let transitionEventType: null | string = null; // Event type of the first
5860
export let transitionEventIsRepeat: boolean = false;
5961
export let transitionSuspendedTime: number = -1.1;
6062

63+
export let yieldReason: SuspendedReason = (0: any);
64+
export let yieldStartTime: number = -1.1; // The time when we yielded to the event loop
65+
66+
export function startYieldTimer(reason: SuspendedReason) {
67+
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
68+
return;
69+
}
70+
yieldStartTime = now();
71+
yieldReason = reason;
72+
}
73+
6174
export function startUpdateTimerByLane(lane: Lane): void {
6275
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
6376
return;

0 commit comments

Comments
 (0)