71
import {
72
logBlockingStart,
73
logTransitionStart,
74
+ logRenderPhase,
75
+ logSuspenseThrottlePhase,
76
+ logSuspendedCommitPhase,
77
+ logCommitPhase,
78
+ logPaintYieldPhase,
79
+ logPassiveCommitPhase,
80
} from './ReactFiberPerformanceTrack';
81
82
import {
245
clampTransitionTimers,
246
markNestedUpdateScheduled,
247
renderStartTime,
248
+ commitStartTime,
249
+ commitEndTime,
250
recordRenderTime,
243
- recordCompleteTime,
251
recordCommitTime,
252
+ recordCommitEndTime,
253
resetNestedUpdateFlag,
254
startProfilerTimer,
255
stopProfilerTimerIfRunningAndRecordDuration,
609
let rootWithPendingPassiveEffects: FiberRoot | null = null;
610
let pendingPassiveEffectsLanes: Lanes = NoLanes;
611
let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes;
612
+let pendingPassiveEffectsRenderEndTime: number = -0; // Profiling-only
613
let pendingPassiveTransitions: Array<Transition> | null = null;
614
615
// Use these to prevent an infinite loop of nested updates
1128
finishedWork: Fiber,
1129
lanes: Lanes,
1130
) {
1131
+ let renderEndTime = 0;
1132
if (enableProfilerTimer && enableComponentPerformanceTrack) {
1133
// Track when we finished the last unit of work, before we actually commit it.
1134
// The commit can be suspended/blocked until we commit it.
1125
- recordCompleteTime();
1135
+ renderEndTime = now();
1136
}
1137
1138
// TODO: The fact that most of these branches are identical suggests that some
1192
workInProgressDeferredLane,
1193
workInProgressRootInterleavedUpdatedLanes,
1194
workInProgressSuspendedRetryLanes,
1195
+ IMMEDIATE_COMMIT,
1196
+ renderStartTime,
1197
+ renderEndTime,
1198
);
1199
} else {
1200
if (
1240
workInProgressRootInterleavedUpdatedLanes,
1241
workInProgressSuspendedRetryLanes,
1242
workInProgressRootDidSkipSuspendedSiblings,
1243
+ THROTTLED_COMMIT,
1244
+ renderStartTime,
1245
+ renderEndTime,
1246
),
1247
msUntilTimeout,
1248
);
1260
workInProgressRootInterleavedUpdatedLanes,
1261
workInProgressSuspendedRetryLanes,
1262
workInProgressRootDidSkipSuspendedSiblings,
1263
+ IMMEDIATE_COMMIT,
1264
+ renderStartTime,
1265
+ renderEndTime,
1266
);
1267
}
1268
}
1278
updatedLanes: Lanes,
1279
suspendedRetryLanes: Lanes,
1280
didSkipSuspendedSiblings: boolean,
1281
+ suspendedCommitReason: SuspendedCommitReason, // Profiling-only
1282
+ completedRenderStartTime: number, // Profiling-only
1283
+ completedRenderEndTime: number, // Profiling-only
1284
) {
1285
// TODO: Combine retry throttling with Suspensey commits. Right now they run
1286
// one after the other.
1321
spawnedLane,
1322
updatedLanes,
1323
suspendedRetryLanes,
1324
+ SUSPENDED_COMMIT,
1325
),
1326
);
1327
markRootSuspended(root, lanes, spawnedLane, didSkipSuspendedSiblings);
1338
spawnedLane,
1339
updatedLanes,
1340
suspendedRetryLanes,
1341
+ suspendedCommitReason,
1342
+ completedRenderStartTime,
1343
+ completedRenderEndTime,
1344
);
1345
}
1346
1532
return null;
1533
}
1534
1535
+ let renderEndTime = 0;
1536
if (enableProfilerTimer && enableComponentPerformanceTrack) {
1510
- recordCompleteTime();
1537
+ renderEndTime = now();
1538
}
1539
1540
// We now have a consistent tree. Because this is a sync render, we
1550
workInProgressDeferredLane,
1551
workInProgressRootInterleavedUpdatedLanes,
1552
workInProgressSuspendedRetryLanes,
1553
+ IMMEDIATE_COMMIT,
1554
+ renderStartTime,
1555
+ renderEndTime,
1556
);
1557
1558
// Before exiting, make sure there's a callback scheduled for the next
3046
workInProgress = null;
3047
}
3048
3049
+type SuspendedCommitReason = 0 | 1 | 2;
3050
+const IMMEDIATE_COMMIT = 0;
3051
+const SUSPENDED_COMMIT = 1;
3052
+const THROTTLED_COMMIT = 2;
3053
+
3054
function commitRoot(
3055
root: FiberRoot,
3056
recoverableErrors: null | Array<CapturedValue<mixed>>,
3059
spawnedLane: Lane,
3060
updatedLanes: Lanes,
3061
suspendedRetryLanes: Lanes,
3062
+ suspendedCommitReason: SuspendedCommitReason, // Profiling-only
3063
+ completedRenderStartTime: number, // Profiling-only
3064
+ completedRenderEndTime: number, // Profiling-only
3065
) {
3066
// TODO: This no longer makes any sense. We already wrap the mutation and
3067
// layout phases. Should be able to remove.
3079
spawnedLane,
3080
updatedLanes,
3081
suspendedRetryLanes,
3082
+ suspendedCommitReason,
3083
+ completedRenderStartTime,
3084
+ completedRenderEndTime,
3085
);
3086
} finally {
3087
ReactSharedInternals.T = prevTransition;
3100
spawnedLane: Lane,
3101
updatedLanes: Lanes,
3102
suspendedRetryLanes: Lanes,
3103
+ suspendedCommitReason: SuspendedCommitReason, // Profiling-only
3104
+ completedRenderStartTime: number, // Profiling-only
3105
+ completedRenderEndTime: number, // Profiling-only
3106
) {
3107
do {
3108
// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
3122
const finishedWork = root.finishedWork;
3123
const lanes = root.finishedLanes;
3124
3125
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
3126
+ // Log the previous render phase once we commit. I.e. we weren't interrupted.
3127
+ setCurrentTrackFromLanes(lanes);
3128
+ logRenderPhase(completedRenderStartTime, completedRenderEndTime);
3129
+ }
3130
+
3131
if (__DEV__) {
3132
if (enableDebugTracing) {
3133
logCommitStarted(lanes);
3224
if (!rootDoesHavePassiveEffects) {
3225
rootDoesHavePassiveEffects = true;
3226
pendingPassiveEffectsRemainingLanes = remainingLanes;
3227
+ pendingPassiveEffectsRenderEndTime = completedRenderEndTime;
3228
// workInProgressTransitions might be overwritten, so we want
3229
// to store it in pendingPassiveTransitions until they get processed
3230
// We need to pass this through as an argument to commitRoot
3233
// with setTimeout
3234
pendingPassiveTransitions = transitions;
3235
scheduleCallback(NormalSchedulerPriority, () => {
3185
- flushPassiveEffects();
3236
+ flushPassiveEffects(true);
3237
// This render triggered passive effects: release the root cache pool
3238
// *after* passive effects fire to avoid freeing a cache pool that may
3239
// be referenced by a node in the tree (HostRoot, Cache boundary etc)
3242
}
3243
}
3244
3245
+ if (enableProfilerTimer) {
3246
+ // Mark the current commit time to be shared by all Profilers in this
3247
+ // batch. This enables them to be grouped later.
3248
+ recordCommitTime();
3249
+ if (enableComponentPerformanceTrack) {
3250
+ if (suspendedCommitReason === SUSPENDED_COMMIT) {
3251
+ logSuspendedCommitPhase(completedRenderEndTime, commitStartTime);
3252
+ } else if (suspendedCommitReason === THROTTLED_COMMIT) {
3253
+ logSuspenseThrottlePhase(completedRenderEndTime, commitStartTime);
3254
+ }
3255
+ }
3256
+ }
3257
+
3258
// Check if there are any effects in the whole tree.
3259
// TODO: This is left over from the effect list implementation, where we had
3260
// to check for the existence of `firstEffect` to satisfy Flow. I think the
3290
finishedWork,
3291
);
3292
3229
- if (enableProfilerTimer) {
3230
- // Mark the current commit time to be shared by all Profilers in this
3231
- // batch. This enables them to be grouped later.
3232
- recordCommitTime();
3233
- }
3234
-
3293
// The next phase is the mutation phase, where we mutate the host tree.
3294
commitMutationEffects(root, finishedWork, lanes);
3295
3340
} else {
3341
// No effects.
3342
root.current = finishedWork;
3285
- // Measure these anyway so the flamegraph explicitly shows that there were
3286
- // no effects.
3287
- // TODO: Maybe there's a better way to report this.
3288
- if (enableProfilerTimer) {
3289
- recordCommitTime();
3290
- }
3343
+ }
3344
+
3345
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
3346
+ recordCommitEndTime();
3347
+ logCommitPhase(commitStartTime, commitEndTime);
3348
}
3349
3350
const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
3561
}
3562
}
3563
3507
-export function flushPassiveEffects(): boolean {
3564
+export function flushPassiveEffects(wasDelayedCommit?: boolean): boolean {
3565
// Returns whether passive effects were flushed.
3566
// TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
3567
// probably just combine the two functions. I believe they were only separate
3586
try {
3587
setCurrentUpdatePriority(priority);
3588
ReactSharedInternals.T = null;
3532
- return flushPassiveEffectsImpl();
3589
+ return flushPassiveEffectsImpl(wasDelayedCommit);
3590
} finally {
3591
setCurrentUpdatePriority(previousPriority);
3592
ReactSharedInternals.T = prevTransition;
3600
return false;
3601
}
3602
3546
-function flushPassiveEffectsImpl() {
3603
+function flushPassiveEffectsImpl(wasDelayedCommit: void | boolean) {
3604
if (rootWithPendingPassiveEffects === null) {
3605
return false;
3606
}
3636
}
3637
}
3638
3639
+ let passiveEffectStartTime = 0;
3640
+ if (enableProfilerTimer && enableComponentPerformanceTrack) {
3641
+ passiveEffectStartTime = now();
3642
+ logPaintYieldPhase(commitEndTime, passiveEffectStartTime);
3643
+ }
3644
+
3645
if (enableSchedulingProfiler) {
3646
markPassiveEffectsStarted(lanes);
3647
}
3650
executionContext |= CommitContext;
3651
3652
commitPassiveUnmountEffects(root.current);
3590
- commitPassiveMountEffects(root, root.current, lanes, transitions);
3653
+ commitPassiveMountEffects(
3654
+ root,
3655
+ root.current,
3656
+ lanes,
3657
+ transitions,
3658
+ pendingPassiveEffectsRenderEndTime,
3659
+ );
3660
3661
if (__DEV__) {
3662
if (enableDebugTracing) {
3675
executionContext = prevExecutionContext;
3676
3677
if (enableProfilerTimer && enableComponentPerformanceTrack) {
3609
- finalizeRender(lanes, now());
3678
+ const passiveEffectsEndTime = now();
3679
+ if (wasDelayedCommit) {
3680
+ logPassiveCommitPhase(passiveEffectStartTime, passiveEffectsEndTime);
3681
+ }
3682
+ finalizeRender(lanes, passiveEffectsEndTime);
3683
}
3684
3685
flushSyncWorkOnAllRoots();