0
# Profiling
1
2
Performance monitoring and event logging capabilities for development and debugging of scheduler-based applications.
3
4
The profiling system provides detailed insights into task execution, timing, and scheduler behavior through structured event logging.
5
6
## Capabilities
7
8
### Profiling Interface
9
10
The main profiling interface provides methods to start and stop profiling event collection.
11
12
```javascript { .api }
13
/**
14
* Profiling interface for performance monitoring
15
* Available only in profiling builds, null in production builds
16
*/
17
const unstable_Profiling: {
18
/** Start collecting profiling events */
19
startLoggingProfilingEvents(): void;
20
/** Stop collecting events and return logged data */
21
stopLoggingProfilingEvents(): ArrayBuffer | null;
22
} | null;
23
```
24
25
**Usage Examples:**
26
27
```javascript
28
import { unstable_Profiling } from "scheduler";
29
30
// Check if profiling is available
31
if (unstable_Profiling !== null) {
32
// Start profiling
33
unstable_Profiling.startLoggingProfilingEvents();
34
35
// Perform operations to profile
36
performScheduledWork();
37
38
// Stop profiling and get data
39
const profilingData = unstable_Profiling.stopLoggingProfilingEvents();
40
41
if (profilingData) {
42
// Process profiling data (ArrayBuffer)
43
analyzeProfilingData(profilingData);
44
}
45
} else {
46
console.log("Profiling not available in this build");
47
}
48
49
// Conditional profiling wrapper
50
function withProfiling(operation, name) {
51
const profilingEnabled = unstable_Profiling !== null;
52
53
if (profilingEnabled) {
54
unstable_Profiling.startLoggingProfilingEvents();
55
}
56
57
try {
58
return operation();
59
} finally {
60
if (profilingEnabled) {
61
const data = unstable_Profiling.stopLoggingProfilingEvents();
62
console.log(`Profiling data for ${name}:`, data);
63
}
64
}
65
}
66
```
67
68
### Profiling Data Format
69
70
The profiling system logs events as structured data in an ArrayBuffer. Events are logged as 32-bit integers with the following format:
71
72
- **Event Type** (1 byte): Type of event that occurred
73
- **Timestamp** (4 bytes): Microsecond timestamp when event occurred
74
- **Task ID** (4 bytes): Unique identifier for the task
75
- **Additional Data** (varies): Priority level, run ID, or other event-specific data
76
77
**Event Types:**
78
79
```javascript { .api }
80
// Event type constants (internal)
81
const TaskStartEvent = 1; // Task added to queue
82
const TaskCompleteEvent = 2; // Task finished successfully
83
const TaskErrorEvent = 3; // Task threw an error
84
const TaskCancelEvent = 4; // Task was cancelled
85
const TaskRunEvent = 5; // Task started executing
86
const TaskYieldEvent = 6; // Task yielded control
87
const SchedulerSuspendEvent = 7; // Scheduler became idle
88
const SchedulerResumeEvent = 8; // Scheduler resumed work
89
```
90
91
## Profiling Events
92
93
The scheduler automatically logs various events during task execution when profiling is enabled:
94
95
### Task Lifecycle Events
96
97
- **Task Start**: When a task is scheduled and added to the queue
98
- **Task Run**: When a task begins executing
99
- **Task Yield**: When a task yields control back to scheduler
100
- **Task Complete**: When a task finishes successfully
101
- **Task Error**: When a task throws an exception
102
- **Task Cancel**: When a task is cancelled before completion
103
104
### Scheduler Events
105
106
- **Scheduler Suspend**: When scheduler becomes idle (no pending work)
107
- **Scheduler Resume**: When scheduler resumes after being idle
108
109
## Advanced Usage
110
111
### Profiling Specific Operations
112
113
```javascript
114
import {
115
unstable_Profiling,
116
unstable_scheduleCallback,
117
unstable_NormalPriority
118
} from "scheduler";
119
120
async function profileTaskExecution() {
121
if (!unstable_Profiling) return;
122
123
unstable_Profiling.startLoggingProfilingEvents();
124
125
// Schedule multiple tasks to profile
126
const tasks = [];
127
for (let i = 0; i < 10; i++) {
128
const task = unstable_scheduleCallback(unstable_NormalPriority, (didTimeout) => {
129
// Simulate work
130
const start = performance.now();
131
while (performance.now() - start < 5) {
132
// Busy wait for 5ms
133
}
134
135
if (i % 3 === 0) {
136
// Some tasks yield
137
return (didTimeout) => {
138
console.log(`Task ${i} continuation, timeout: ${didTimeout}`);
139
};
140
}
141
});
142
tasks.push(task);
143
}
144
145
// Let tasks execute, then collect profiling data
146
setTimeout(() => {
147
const profilingData = unstable_Profiling.stopLoggingProfilingEvents();
148
console.log("Profiling completed, data size:", profilingData?.byteLength);
149
}, 1000);
150
}
151
```
152
153
### Profiling Data Analysis
154
155
```javascript
156
function analyzeProfilingData(arrayBuffer) {
157
if (!arrayBuffer || arrayBuffer.byteLength === 0) {
158
console.log("No profiling data available");
159
return;
160
}
161
162
const view = new Int32Array(arrayBuffer);
163
const events = [];
164
165
// Parse events from the buffer
166
for (let i = 0; i < view.length; i += 4) {
167
const eventType = view[i];
168
const timestamp = view[i + 1];
169
const taskId = view[i + 2];
170
const extraData = view[i + 3];
171
172
events.push({
173
type: getEventTypeName(eventType),
174
timestamp: timestamp / 1000, // Convert microseconds to milliseconds
175
taskId,
176
extraData
177
});
178
}
179
180
// Analyze the events
181
console.log(`Collected ${events.length} profiling events`);
182
183
const taskMetrics = calculateTaskMetrics(events);
184
console.log("Task metrics:", taskMetrics);
185
}
186
187
function getEventTypeName(eventType) {
188
const names = {
189
1: "TaskStart",
190
2: "TaskComplete",
191
3: "TaskError",
192
4: "TaskCancel",
193
5: "TaskRun",
194
6: "TaskYield",
195
7: "SchedulerSuspend",
196
8: "SchedulerResume"
197
};
198
return names[eventType] || "Unknown";
199
}
200
201
function calculateTaskMetrics(events) {
202
const taskStats = new Map();
203
204
events.forEach(event => {
205
if (!taskStats.has(event.taskId)) {
206
taskStats.set(event.taskId, {
207
startTime: null,
208
endTime: null,
209
runTime: null,
210
yields: 0,
211
errors: 0
212
});
213
}
214
215
const stats = taskStats.get(event.taskId);
216
217
switch (event.type) {
218
case "TaskStart":
219
stats.startTime = event.timestamp;
220
break;
221
case "TaskRun":
222
stats.runTime = event.timestamp;
223
break;
224
case "TaskComplete":
225
stats.endTime = event.timestamp;
226
break;
227
case "TaskYield":
228
stats.yields++;
229
break;
230
case "TaskError":
231
stats.errors++;
232
break;
233
}
234
});
235
236
return {
237
totalTasks: taskStats.size,
238
avgExecutionTime: calculateAverageExecutionTime(taskStats),
239
totalYields: Array.from(taskStats.values()).reduce((sum, stats) => sum + stats.yields, 0),
240
totalErrors: Array.from(taskStats.values()).reduce((sum, stats) => sum + stats.errors, 0)
241
};
242
}
243
```
244
245
### Integration with Development Tools
246
247
```javascript
248
// Development-only profiling wrapper
249
function createProfilingWrapper() {
250
if (process.env.NODE_ENV !== "development" || !unstable_Profiling) {
251
return {
252
start: () => {},
253
end: () => {},
254
wrap: (fn) => fn
255
};
256
}
257
258
let isActive = false;
259
260
return {
261
start(label = "operation") {
262
if (isActive) return;
263
isActive = true;
264
console.log(`Starting profiling: ${label}`);
265
unstable_Profiling.startLoggingProfilingEvents();
266
},
267
268
end(label = "operation") {
269
if (!isActive) return;
270
isActive = false;
271
const data = unstable_Profiling.stopLoggingProfilingEvents();
272
console.log(`Profiling completed: ${label}`, data);
273
},
274
275
wrap(fn, label) {
276
return (...args) => {
277
this.start(label);
278
try {
279
return fn(...args);
280
} finally {
281
this.end(label);
282
}
283
};
284
}
285
};
286
}
287
288
// Usage
289
const profiler = createProfilingWrapper();
290
291
const profiledFunction = profiler.wrap(
292
() => {
293
// Scheduled work
294
performComplexOperation();
295
},
296
"complex-operation"
297
);
298
```
299
300
## Build Configuration
301
302
Profiling is controlled at build time through feature flags:
303
304
- **Development builds**: Profiling typically enabled
305
- **Production builds**: Profiling disabled (`unstable_Profiling` is `null`)
306
- **Profiling builds**: Special builds with profiling always enabled
307
308
The profiling system adds minimal overhead when disabled, but can impact performance when enabled due to event logging overhead.
309
310
## Limitations
311
312
1. **Build Dependency**: Only available in builds with profiling enabled
313
2. **Memory Usage**: Profiling data accumulates in memory until stopped
314
3. **Performance Impact**: Event logging adds overhead to task execution
315
4. **Data Size Limits**: Maximum event log size is limited (2MB by default)
316
5. **Binary Format**: Profiling data is in binary format requiring parsing
317
318
## Best Practices
319
320
- Enable profiling only during development and debugging
321
- Stop profiling collection regularly to prevent memory buildup
322
- Use profiling to identify performance bottlenecks in task scheduling
323
- Combine with browser DevTools for comprehensive performance analysis
324
- Profile both individual operations and complete workflows