Skip to content

Commit cd1b167

Browse files
authored
[Scheduler Profiler] Use microsecond precision (#17010)
The `performance.now` returns a timestamp in milliseconds as a float. The browser has the option to adjust the precision of the float, but it's usually more precise than a millisecond. However, this precision is lost when the timestamp is logged by the Scheduler profiler, because we store the numbers in an Int32Array. This change multiplies the millisecond float value by 1000, giving us three more degrees of precision.
1 parent 55731fd commit cd1b167

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

packages/scheduler/src/SchedulerProfiling.js

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,16 @@ export function stopLoggingProfilingEvents(): ArrayBuffer | null {
103103

104104
export function markTaskStart(
105105
task: {id: number, priorityLevel: PriorityLevel},
106-
time: number,
106+
ms: number,
107107
) {
108108
if (enableProfiling) {
109109
profilingState[QUEUE_SIZE]++;
110110

111111
if (eventLog !== null) {
112-
logEvent([TaskStartEvent, time, task.id, task.priorityLevel]);
112+
// performance.now returns a float, representing milliseconds. When the
113+
// event is logged, it's coerced to an int. Convert to microseconds to
114+
// maintain extra degrees of precision.
115+
logEvent([TaskStartEvent, ms * 1000, task.id, task.priorityLevel]);
113116
}
114117
}
115118
}
@@ -119,15 +122,15 @@ export function markTaskCompleted(
119122
id: number,
120123
priorityLevel: PriorityLevel,
121124
},
122-
time: number,
125+
ms: number,
123126
) {
124127
if (enableProfiling) {
125128
profilingState[PRIORITY] = NoPriority;
126129
profilingState[CURRENT_TASK_ID] = 0;
127130
profilingState[QUEUE_SIZE]--;
128131

129132
if (eventLog !== null) {
130-
logEvent([TaskCompleteEvent, time, task.id]);
133+
logEvent([TaskCompleteEvent, ms * 1000, task.id]);
131134
}
132135
}
133136
}
@@ -137,13 +140,13 @@ export function markTaskCanceled(
137140
id: number,
138141
priorityLevel: PriorityLevel,
139142
},
140-
time: number,
143+
ms: number,
141144
) {
142145
if (enableProfiling) {
143146
profilingState[QUEUE_SIZE]--;
144147

145148
if (eventLog !== null) {
146-
logEvent([TaskCancelEvent, time, task.id]);
149+
logEvent([TaskCancelEvent, ms * 1000, task.id]);
147150
}
148151
}
149152
}
@@ -153,22 +156,22 @@ export function markTaskErrored(
153156
id: number,
154157
priorityLevel: PriorityLevel,
155158
},
156-
time: number,
159+
ms: number,
157160
) {
158161
if (enableProfiling) {
159162
profilingState[PRIORITY] = NoPriority;
160163
profilingState[CURRENT_TASK_ID] = 0;
161164
profilingState[QUEUE_SIZE]--;
162165

163166
if (eventLog !== null) {
164-
logEvent([TaskErrorEvent, time, task.id]);
167+
logEvent([TaskErrorEvent, ms * 1000, task.id]);
165168
}
166169
}
167170
}
168171

169172
export function markTaskRun(
170173
task: {id: number, priorityLevel: PriorityLevel},
171-
time: number,
174+
ms: number,
172175
) {
173176
if (enableProfiling) {
174177
runIdCounter++;
@@ -178,37 +181,37 @@ export function markTaskRun(
178181
profilingState[CURRENT_RUN_ID] = runIdCounter;
179182

180183
if (eventLog !== null) {
181-
logEvent([TaskRunEvent, time, task.id, runIdCounter]);
184+
logEvent([TaskRunEvent, ms * 1000, task.id, runIdCounter]);
182185
}
183186
}
184187
}
185188

186-
export function markTaskYield(task: {id: number}, time: number) {
189+
export function markTaskYield(task: {id: number}, ms: number) {
187190
if (enableProfiling) {
188191
profilingState[PRIORITY] = NoPriority;
189192
profilingState[CURRENT_TASK_ID] = 0;
190193
profilingState[CURRENT_RUN_ID] = 0;
191194

192195
if (eventLog !== null) {
193-
logEvent([TaskYieldEvent, time, task.id, runIdCounter]);
196+
logEvent([TaskYieldEvent, ms * 1000, task.id, runIdCounter]);
194197
}
195198
}
196199
}
197200

198-
export function markSchedulerSuspended(time: number) {
201+
export function markSchedulerSuspended(ms: number) {
199202
if (enableProfiling) {
200203
mainThreadIdCounter++;
201204

202205
if (eventLog !== null) {
203-
logEvent([SchedulerSuspendEvent, time, mainThreadIdCounter]);
206+
logEvent([SchedulerSuspendEvent, ms * 1000, mainThreadIdCounter]);
204207
}
205208
}
206209
}
207210

208-
export function markSchedulerUnsuspended(time: number) {
211+
export function markSchedulerUnsuspended(ms: number) {
209212
if (enableProfiling) {
210213
if (eventLog !== null) {
211-
logEvent([SchedulerResumeEvent, time, mainThreadIdCounter]);
214+
logEvent([SchedulerResumeEvent, ms * 1000, mainThreadIdCounter]);
212215
}
213216
}
214217
}

packages/scheduler/src/__tests__/SchedulerProfiling-test.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,16 @@ describe('Scheduler', () => {
213213

214214
// Now we can render the tasks as a flamegraph.
215215
const labelColumnWidth = 30;
216-
const msPerChar = 50;
216+
// Scheduler event times are in microseconds
217+
const microsecondsPerChar = 50000;
217218

218219
let result = '';
219220

220221
const mainThreadLabelColumn = '!!! Main thread ';
221222
let mainThreadTimelineColumn = '';
222223
let isMainThreadBusy = true;
223224
for (const time of mainThreadRuns) {
224-
const index = time / msPerChar;
225+
const index = time / microsecondsPerChar;
225226
mainThreadTimelineColumn += (isMainThreadBusy ? '█' : '░').repeat(
226227
index - mainThreadTimelineColumn.length,
227228
);
@@ -244,18 +245,18 @@ describe('Scheduler', () => {
244245
labelColumn += ' '.repeat(labelColumnWidth - labelColumn.length - 1);
245246

246247
// Add empty space up until the start mark
247-
let timelineColumn = ' '.repeat(task.start / msPerChar);
248+
let timelineColumn = ' '.repeat(task.start / microsecondsPerChar);
248249

249250
let isRunning = false;
250251
for (const time of task.runs) {
251-
const index = time / msPerChar;
252+
const index = time / microsecondsPerChar;
252253
timelineColumn += (isRunning ? '█' : '░').repeat(
253254
index - timelineColumn.length,
254255
);
255256
isRunning = !isRunning;
256257
}
257258

258-
const endIndex = task.end / msPerChar;
259+
const endIndex = task.end / microsecondsPerChar;
259260
timelineColumn += (isRunning ? '█' : '░').repeat(
260261
endIndex - timelineColumn.length,
261262
);

0 commit comments

Comments
 (0)