@samitouri / QOS-React / commits / 8657869999

Separate Tracks for Components and Phases (#31525)

Previously we were showing Components inside each lane track but that meant that as soon as you expanded a lane you couldn't see the other line so you couldn't get an overview over how well things were scheduled. This instead moves all the Components into a single top-level track and renames the previous one to a "Scheduler" track group. <img width="1352" alt="Screenshot 2024-11-12 at 8 26 05 PM" src="https://github.com/user-attachments/assets/590bc6d3-3540-4ee4-b474-5d733b8d8d8d"> That way you can get an overview over what React is working on first and then right below see which Component is being worked on. Ideally the "Scheduler" track would be always expanded since each Track is always just a single row. Now you have to expand each lane to see the labels but then you're wasting a lot of vertical real estate. There's currently no option to create this with the Chrome performance.measure extensions. <img width="1277" alt="Screenshot 2024-11-12 at 8 26 16 PM" src="https://github.com/user-attachments/assets/4fc39e35-10ec-4452-ad32-c1c2e6b5e1a8">

Sebastian Markbåge committed Nov 14, 2024 at 13:05 UTC 8657869999d3ed36cde45a4b2ef33fc815869295
1 file changed +65 -53
packages/react-reconciler/src/ReactFiberPerformanceTrack.js
+65 -53
@@ -21,14 +21,13 @@ const supportsUserTiming =
21 // $FlowFixMe[method-unbinding]
22 typeof performance.measure === 'function';
23
24 -const TRACK_GROUP = 'Components ⚛';
24 +const COMPONENTS_TRACK = 'Components ⚛';
25
26 // Reused to avoid thrashing the GC.
27 const reusableComponentDevToolDetails = {
28 dataType: 'track-entry',
29 color: 'primary',
30 - track: 'Blocking', // Lane
31 - trackGroup: TRACK_GROUP,
30 + track: COMPONENTS_TRACK,
31 };
32 const reusableComponentOptions = {
33 start: -0,
@@ -38,9 +37,24 @@ const reusableComponentOptions = {
37 },
38 };
39
40 +const LANES_TRACK_GROUP = 'Scheduler ⚛';
41 +
42 +const reusableLaneDevToolDetails = {
43 + dataType: 'track-entry',
44 + color: 'primary',
45 + track: 'Blocking', // Lane
46 + trackGroup: LANES_TRACK_GROUP,
47 +};
48 +const reusableLaneOptions = {
49 + start: -0,
50 + end: -0,
51 + detail: {
52 + devtools: reusableLaneDevToolDetails,
53 + },
54 +};
55 +
56 export function setCurrentTrackFromLanes(lanes: number): void {
42 - reusableComponentDevToolDetails.track =
43 - getGroupNameOfHighestPriorityLane(lanes);
57 + reusableLaneDevToolDetails.track = getGroupNameOfHighestPriorityLane(lanes);
58 }
59
60 export function logComponentRender(
@@ -107,21 +121,20 @@ export function logBlockingStart(
121 renderStartTime: number,
122 ): void {
123 if (supportsUserTiming) {
110 - reusableComponentDevToolDetails.track = 'Blocking';
124 + reusableLaneDevToolDetails.track = 'Blocking';
125 if (eventTime > 0 && eventType !== null) {
126 // Log the time from the event timeStamp until we called setState.
113 - reusableComponentDevToolDetails.color = 'secondary-dark';
114 - reusableComponentOptions.start = eventTime;
115 - reusableComponentOptions.end =
116 - updateTime > 0 ? updateTime : renderStartTime;
117 - performance.measure(eventType, reusableComponentOptions);
127 + reusableLaneDevToolDetails.color = 'secondary-dark';
128 + reusableLaneOptions.start = eventTime;
129 + reusableLaneOptions.end = updateTime > 0 ? updateTime : renderStartTime;
130 + performance.measure(eventType, reusableLaneOptions);
131 }
132 if (updateTime > 0) {
133 // Log the time from when we called setState until we started rendering.
121 - reusableComponentDevToolDetails.color = 'primary-light';
122 - reusableComponentOptions.start = updateTime;
123 - reusableComponentOptions.end = renderStartTime;
124 - performance.measure('Blocked', reusableComponentOptions);
134 + reusableLaneDevToolDetails.color = 'primary-light';
135 + reusableLaneOptions.start = updateTime;
136 + reusableLaneOptions.end = renderStartTime;
137 + performance.measure('Blocked', reusableLaneOptions);
138 }
139 }
140 }
@@ -134,43 +147,42 @@ export function logTransitionStart(
147 renderStartTime: number,
148 ): void {
149 if (supportsUserTiming) {
137 - reusableComponentDevToolDetails.track = 'Transition';
150 + reusableLaneDevToolDetails.track = 'Transition';
151 if (eventTime > 0 && eventType !== null) {
152 // Log the time from the event timeStamp until we started a transition.
140 - reusableComponentDevToolDetails.color = 'secondary-dark';
141 - reusableComponentOptions.start = eventTime;
142 - reusableComponentOptions.end =
153 + reusableLaneDevToolDetails.color = 'secondary-dark';
154 + reusableLaneOptions.start = eventTime;
155 + reusableLaneOptions.end =
156 startTime > 0
157 ? startTime
158 : updateTime > 0
159 ? updateTime
160 : renderStartTime;
148 - performance.measure(eventType, reusableComponentOptions);
161 + performance.measure(eventType, reusableLaneOptions);
162 }
163 if (startTime > 0) {
164 // Log the time from when we started an async transition until we called setState or started rendering.
152 - reusableComponentDevToolDetails.color = 'primary-dark';
153 - reusableComponentOptions.start = startTime;
154 - reusableComponentOptions.end =
155 - updateTime > 0 ? updateTime : renderStartTime;
156 - performance.measure('Action', reusableComponentOptions);
165 + reusableLaneDevToolDetails.color = 'primary-dark';
166 + reusableLaneOptions.start = startTime;
167 + reusableLaneOptions.end = updateTime > 0 ? updateTime : renderStartTime;
168 + performance.measure('Action', reusableLaneOptions);
169 }
170 if (updateTime > 0) {
171 // Log the time from when we called setState until we started rendering.
160 - reusableComponentDevToolDetails.color = 'primary-light';
161 - reusableComponentOptions.start = updateTime;
162 - reusableComponentOptions.end = renderStartTime;
163 - performance.measure('Blocked', reusableComponentOptions);
172 + reusableLaneDevToolDetails.color = 'primary-light';
173 + reusableLaneOptions.start = updateTime;
174 + reusableLaneOptions.end = renderStartTime;
175 + performance.measure('Blocked', reusableLaneOptions);
176 }
177 }
178 }
179
180 export function logRenderPhase(startTime: number, endTime: number): void {
181 if (supportsUserTiming) {
170 - reusableComponentDevToolDetails.color = 'primary-dark';
171 - reusableComponentOptions.start = startTime;
172 - reusableComponentOptions.end = endTime;
173 - performance.measure('Render', reusableComponentOptions);
182 + reusableLaneDevToolDetails.color = 'primary-dark';
183 + reusableLaneOptions.start = startTime;
184 + reusableLaneOptions.end = endTime;
185 + performance.measure('Render', reusableLaneOptions);
186 }
187 }
188
@@ -180,10 +192,10 @@ export function logSuspenseThrottlePhase(
192 ): void {
193 // This was inside a throttled Suspense boundary commit.
194 if (supportsUserTiming) {
183 - reusableComponentDevToolDetails.color = 'secondary-light';
184 - reusableComponentOptions.start = startTime;
185 - reusableComponentOptions.end = endTime;
186 - performance.measure('Throttled', reusableComponentOptions);
195 + reusableLaneDevToolDetails.color = 'secondary-light';
196 + reusableLaneOptions.start = startTime;
197 + reusableLaneOptions.end = endTime;
198 + performance.measure('Throttled', reusableLaneOptions);
199 }
200 }
201
@@ -193,28 +205,28 @@ export function logSuspendedCommitPhase(
205 ): void {
206 // This means the commit was suspended on CSS or images.
207 if (supportsUserTiming) {
196 - reusableComponentDevToolDetails.color = 'secondary-light';
197 - reusableComponentOptions.start = startTime;
198 - reusableComponentOptions.end = endTime;
199 - performance.measure('Suspended', reusableComponentOptions);
208 + reusableLaneDevToolDetails.color = 'secondary-light';
209 + reusableLaneOptions.start = startTime;
210 + reusableLaneOptions.end = endTime;
211 + performance.measure('Suspended', reusableLaneOptions);
212 }
213 }
214
215 export function logCommitPhase(startTime: number, endTime: number): void {
216 if (supportsUserTiming) {
205 - reusableComponentDevToolDetails.color = 'secondary-dark';
206 - reusableComponentOptions.start = startTime;
207 - reusableComponentOptions.end = endTime;
208 - performance.measure('Commit', reusableComponentOptions);
217 + reusableLaneDevToolDetails.color = 'secondary-dark';
218 + reusableLaneOptions.start = startTime;
219 + reusableLaneOptions.end = endTime;
220 + performance.measure('Commit', reusableLaneOptions);
221 }
222 }
223
224 export function logPaintYieldPhase(startTime: number, endTime: number): void {
225 if (supportsUserTiming) {
214 - reusableComponentDevToolDetails.color = 'secondary-light';
215 - reusableComponentOptions.start = startTime;
216 - reusableComponentOptions.end = endTime;
217 - performance.measure('Waiting for Paint', reusableComponentOptions);
226 + reusableLaneDevToolDetails.color = 'secondary-light';
227 + reusableLaneOptions.start = startTime;
228 + reusableLaneOptions.end = endTime;
229 + performance.measure('Waiting for Paint', reusableLaneOptions);
230 }
231 }
232
@@ -223,9 +235,9 @@ export function logPassiveCommitPhase(
235 endTime: number,
236 ): void {
237 if (supportsUserTiming) {
226 - reusableComponentDevToolDetails.color = 'secondary-dark';
227 - reusableComponentOptions.start = startTime;
228 - reusableComponentOptions.end = endTime;
229 - performance.measure('Remaining Effects', reusableComponentOptions);
238 + reusableLaneDevToolDetails.color = 'secondary-dark';
239 + reusableLaneOptions.start = startTime;
240 + reusableLaneOptions.end = endTime;
241 + performance.measure('Remaining Effects', reusableLaneOptions);
242 }
243 }