Skip to content

Commit e1c2090

Browse files
authored
[Fiber] Log Component Effects to Performance Track (#30983)
Stacked on #30981. Same as #30967 but for effects. This logs a tree of components using `performance.measure()`. In addition to the previous render phase this logs one tree for each commit phase: - Mutation Phase - Layout Effect - Passive Unmounts - Passive Mounts I currently skip the Before Mutation phase since the snapshots are so unusual it's not worth creating trees for those. The mechanism is that I reuse the timings we track for `enableProfilerCommitHooks`. I track first and last effect timestamp within each component subtree. Then on the way up do we log the entry. This means that we don't include overhead to find our way down to a component and that we don't need to add any additional overhead by reading timestamps. To ensure that the entries get ordered correctly we need to ensure that the start time of each parent is slightly before the inner one.
1 parent 15da917 commit e1c2090

File tree

3 files changed

+215
-88
lines changed

3 files changed

+215
-88
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 112 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,21 @@ import {
9999
Cloned,
100100
} from './ReactFiberFlags';
101101
import {
102-
getCommitTime,
103-
getCompleteTime,
102+
commitTime,
103+
completeTime,
104104
pushNestedEffectDurations,
105105
popNestedEffectDurations,
106106
bubbleNestedEffectDurations,
107+
resetComponentEffectTimers,
108+
pushComponentEffectStart,
109+
popComponentEffectStart,
110+
componentEffectStartTime,
111+
componentEffectEndTime,
107112
} from './ReactProfilerTimer';
108-
import {logComponentRender} from './ReactFiberPerformanceTrack';
113+
import {
114+
logComponentRender,
115+
logComponentEffect,
116+
} from './ReactFiberPerformanceTrack';
109117
import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
110118
import {deferHiddenCallbacks} from './ReactFiberClassUpdateQueue';
111119
import {
@@ -382,6 +390,8 @@ function commitLayoutEffectOnFiber(
382390
finishedWork: Fiber,
383391
committedLanes: Lanes,
384392
): void {
393+
const prevEffectStart = pushComponentEffectStart();
394+
385395
// When updating this function, also update reappearLayoutEffects, which does
386396
// most of the same things when an offscreen tree goes from hidden -> visible.
387397
const flags = finishedWork.flags;
@@ -494,7 +504,7 @@ function commitLayoutEffectOnFiber(
494504
commitProfilerUpdate(
495505
finishedWork,
496506
current,
497-
getCommitTime(),
507+
commitTime,
498508
profilerInstance.effectDuration,
499509
);
500510
} else {
@@ -585,6 +595,23 @@ function commitLayoutEffectOnFiber(
585595
break;
586596
}
587597
}
598+
599+
if (
600+
enableProfilerTimer &&
601+
enableProfilerCommitHooks &&
602+
enableComponentPerformanceTrack &&
603+
(finishedWork.mode & ProfileMode) !== NoMode &&
604+
componentEffectStartTime >= 0 &&
605+
componentEffectEndTime >= 0
606+
) {
607+
logComponentEffect(
608+
finishedWork,
609+
componentEffectStartTime,
610+
componentEffectEndTime,
611+
);
612+
}
613+
614+
popComponentEffectStart(prevEffectStart);
588615
}
589616

590617
function abortRootTransitions(
@@ -1530,6 +1557,8 @@ export function commitMutationEffects(
15301557
inProgressLanes = committedLanes;
15311558
inProgressRoot = root;
15321559

1560+
resetComponentEffectTimers();
1561+
15331562
commitMutationEffectsOnFiber(finishedWork, root, committedLanes);
15341563

15351564
inProgressLanes = null;
@@ -1570,6 +1599,8 @@ function commitMutationEffectsOnFiber(
15701599
root: FiberRoot,
15711600
lanes: Lanes,
15721601
) {
1602+
const prevEffectStart = pushComponentEffectStart();
1603+
15731604
const current = finishedWork.alternate;
15741605
const flags = finishedWork.flags;
15751606

@@ -1598,7 +1629,7 @@ function commitMutationEffectsOnFiber(
15981629
HookLayout | HookHasEffect,
15991630
);
16001631
}
1601-
return;
1632+
break;
16021633
}
16031634
case ClassComponent: {
16041635
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
@@ -1617,7 +1648,7 @@ function commitMutationEffectsOnFiber(
16171648
deferHiddenCallbacks(updateQueue);
16181649
}
16191650
}
1620-
return;
1651+
break;
16211652
}
16221653
case HostHoistable: {
16231654
if (supportsResources) {
@@ -1693,7 +1724,7 @@ function commitMutationEffectsOnFiber(
16931724
);
16941725
}
16951726
}
1696-
return;
1727+
break;
16971728
}
16981729
// Fall through
16991730
}
@@ -1756,7 +1787,7 @@ function commitMutationEffectsOnFiber(
17561787
}
17571788
}
17581789
}
1759-
return;
1790+
break;
17601791
}
17611792
case HostText: {
17621793
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
@@ -1781,7 +1812,7 @@ function commitMutationEffectsOnFiber(
17811812
commitHostTextUpdate(finishedWork, newText, oldText);
17821813
}
17831814
}
1784-
return;
1815+
break;
17851816
}
17861817
case HostRoot: {
17871818
const prevEffectDuration = pushNestedEffectDurations();
@@ -1833,7 +1864,7 @@ function commitMutationEffectsOnFiber(
18331864
root.effectDuration += popNestedEffectDurations(prevEffectDuration);
18341865
}
18351866

1836-
return;
1867+
break;
18371868
}
18381869
case HostPortal: {
18391870
if (supportsResources) {
@@ -1858,7 +1889,7 @@ function commitMutationEffectsOnFiber(
18581889
);
18591890
}
18601891
}
1861-
return;
1892+
break;
18621893
}
18631894
case Profiler: {
18641895
const prevEffectDuration = pushNestedEffectDurations();
@@ -1873,7 +1904,7 @@ function commitMutationEffectsOnFiber(
18731904
profilerInstance.effectDuration +=
18741905
bubbleNestedEffectDurations(prevEffectDuration);
18751906
}
1876-
return;
1907+
break;
18771908
}
18781909
case SuspenseComponent: {
18791910
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
@@ -1925,7 +1956,7 @@ function commitMutationEffectsOnFiber(
19251956
attachSuspenseRetryListeners(finishedWork, retryQueue);
19261957
}
19271958
}
1928-
return;
1959+
break;
19291960
}
19301961
case OffscreenComponent: {
19311962
if (flags & Ref) {
@@ -2018,7 +2049,7 @@ function commitMutationEffectsOnFiber(
20182049
}
20192050
}
20202051
}
2021-
return;
2052+
break;
20222053
}
20232054
case SuspenseListComponent: {
20242055
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
@@ -2032,7 +2063,7 @@ function commitMutationEffectsOnFiber(
20322063
attachSuspenseRetryListeners(finishedWork, retryQueue);
20332064
}
20342065
}
2035-
return;
2066+
break;
20362067
}
20372068
case ScopeComponent: {
20382069
if (enableScopeAPI) {
@@ -2052,16 +2083,34 @@ function commitMutationEffectsOnFiber(
20522083
prepareScopeUpdate(scopeInstance, finishedWork);
20532084
}
20542085
}
2055-
return;
2086+
break;
20562087
}
20572088
default: {
20582089
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
20592090
commitReconciliationEffects(finishedWork);
20602091

2061-
return;
2092+
break;
20622093
}
20632094
}
2095+
2096+
if (
2097+
enableProfilerTimer &&
2098+
enableProfilerCommitHooks &&
2099+
enableComponentPerformanceTrack &&
2100+
(finishedWork.mode & ProfileMode) !== NoMode &&
2101+
componentEffectStartTime >= 0 &&
2102+
componentEffectEndTime >= 0
2103+
) {
2104+
logComponentEffect(
2105+
finishedWork,
2106+
componentEffectStartTime,
2107+
componentEffectEndTime,
2108+
);
2109+
}
2110+
2111+
popComponentEffectStart(prevEffectStart);
20642112
}
2113+
20652114
function commitReconciliationEffects(finishedWork: Fiber) {
20662115
// Placement effects (insertions, reorders) can be scheduled on any fiber
20672116
// type. They needs to happen after the children effects have fired, but
@@ -2106,6 +2155,8 @@ export function commitLayoutEffects(
21062155
inProgressLanes = committedLanes;
21072156
inProgressRoot = root;
21082157

2158+
resetComponentEffectTimers();
2159+
21092160
const current = finishedWork.alternate;
21102161
commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);
21112162

@@ -2291,7 +2342,7 @@ export function reappearLayoutEffects(
22912342
commitProfilerUpdate(
22922343
finishedWork,
22932344
current,
2294-
getCommitTime(),
2345+
commitTime,
22952346
profilerInstance.effectDuration,
22962347
);
22972348
} else {
@@ -2515,14 +2566,14 @@ export function commitPassiveMountEffects(
25152566
committedLanes: Lanes,
25162567
committedTransitions: Array<Transition> | null,
25172568
): void {
2569+
resetComponentEffectTimers();
2570+
25182571
commitPassiveMountOnFiber(
25192572
root,
25202573
finishedWork,
25212574
committedLanes,
25222575
committedTransitions,
2523-
enableProfilerTimer && enableComponentPerformanceTrack
2524-
? getCompleteTime()
2525-
: 0,
2576+
enableProfilerTimer && enableComponentPerformanceTrack ? completeTime : 0,
25262577
);
25272578
}
25282579

@@ -2577,6 +2628,8 @@ function commitPassiveMountOnFiber(
25772628
committedTransitions: Array<Transition> | null,
25782629
endTime: number, // Profiling-only. The start time of the next Fiber or root completion.
25792630
): void {
2631+
const prevEffectStart = pushComponentEffectStart();
2632+
25802633
// If this component rendered in Profiling mode (DEV or in Profiler component) then log its
25812634
// render time. We do this after the fact in the passive effect to avoid the overhead of this
25822635
// getting in the way of the render characteristics and avoid the overhead of unwinding
@@ -2707,7 +2760,7 @@ function commitPassiveMountOnFiber(
27072760
finishedWork.alternate,
27082761
// This value will still reflect the previous commit phase.
27092762
// It does not get reset until the start of the next commit phase.
2710-
getCommitTime(),
2763+
commitTime,
27112764
profilerInstance.passiveEffectDuration,
27122765
);
27132766
} else {
@@ -2860,6 +2913,23 @@ function commitPassiveMountOnFiber(
28602913
break;
28612914
}
28622915
}
2916+
2917+
if (
2918+
enableProfilerTimer &&
2919+
enableProfilerCommitHooks &&
2920+
enableComponentPerformanceTrack &&
2921+
(finishedWork.mode & ProfileMode) !== NoMode &&
2922+
componentEffectStartTime >= 0 &&
2923+
componentEffectEndTime >= 0
2924+
) {
2925+
logComponentEffect(
2926+
finishedWork,
2927+
componentEffectStartTime,
2928+
componentEffectEndTime,
2929+
);
2930+
}
2931+
2932+
popComponentEffectStart(prevEffectStart);
28632933
}
28642934

28652935
function recursivelyTraverseReconnectPassiveEffects(
@@ -3131,6 +3201,7 @@ function commitAtomicPassiveEffects(
31313201
}
31323202

31333203
export function commitPassiveUnmountEffects(finishedWork: Fiber): void {
3204+
resetComponentEffectTimers();
31343205
commitPassiveUnmountOnFiber(finishedWork);
31353206
}
31363207

@@ -3289,6 +3360,8 @@ function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
32893360
}
32903361

32913362
function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
3363+
const prevEffectStart = pushComponentEffectStart();
3364+
32923365
switch (finishedWork.tag) {
32933366
case FunctionComponent:
32943367
case ForwardRef:
@@ -3358,6 +3431,23 @@ function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
33583431
break;
33593432
}
33603433
}
3434+
3435+
if (
3436+
enableProfilerTimer &&
3437+
enableProfilerCommitHooks &&
3438+
enableComponentPerformanceTrack &&
3439+
(finishedWork.mode & ProfileMode) !== NoMode &&
3440+
componentEffectStartTime >= 0 &&
3441+
componentEffectEndTime >= 0
3442+
) {
3443+
logComponentEffect(
3444+
finishedWork,
3445+
componentEffectStartTime,
3446+
componentEffectEndTime,
3447+
);
3448+
}
3449+
3450+
popComponentEffectStart(prevEffectStart);
33613451
}
33623452

33633453
function recursivelyTraverseDisconnectPassiveEffects(parentFiber: Fiber): void {

packages/react-reconciler/src/ReactFiberPerformanceTrack.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,24 @@ const reusableComponentOptions = {
3838
},
3939
};
4040

41+
const reusableComponentEffectDevToolDetails = {
42+
dataType: 'track-entry',
43+
color: 'secondary',
44+
track: 'Blocking', // Lane
45+
trackGroup: TRACK_GROUP,
46+
};
47+
const reusableComponentEffectOptions = {
48+
start: -0,
49+
end: -0,
50+
detail: {
51+
devtools: reusableComponentEffectDevToolDetails,
52+
},
53+
};
54+
4155
export function setCurrentTrackFromLanes(lanes: number): void {
42-
reusableComponentDevToolDetails.track =
43-
getGroupNameOfHighestPriorityLane(lanes);
56+
reusableComponentEffectDevToolDetails.track =
57+
reusableComponentDevToolDetails.track =
58+
getGroupNameOfHighestPriorityLane(lanes);
4459
}
4560

4661
export function logComponentRender(
@@ -59,3 +74,20 @@ export function logComponentRender(
5974
performance.measure(name, reusableComponentOptions);
6075
}
6176
}
77+
78+
export function logComponentEffect(
79+
fiber: Fiber,
80+
startTime: number,
81+
endTime: number,
82+
): void {
83+
const name = getComponentNameFromFiber(fiber);
84+
if (name === null) {
85+
// Skip
86+
return;
87+
}
88+
if (supportsUserTiming) {
89+
reusableComponentEffectOptions.start = startTime;
90+
reusableComponentEffectOptions.end = endTime;
91+
performance.measure(name, reusableComponentEffectOptions);
92+
}
93+
}

0 commit comments

Comments
 (0)