feat(tools-performance): Add @rnx-kit/tools-performance package#4079
feat(tools-performance): Add @rnx-kit/tools-performance package#4079
Conversation
| * Check if tracking is enabled for a specific category. If category is undefined, checks if all tracking is enabled. | ||
| */ | ||
| export function isTrackingEnabled(category?: string) { | ||
| return defaultManager?.isEnabled(category) ?? false; |
There was a problem hiding this comment.
| return defaultManager?.isEnabled(category) ?? false; | |
| return Boolean(defaultManager?.isEnabled(category)); |
| const start = performance.now(); | ||
| const result = callFunction<TFunc>(fn, args); | ||
| if (isPromiseLike(result)) { | ||
| return result.then((res: ReturnType<TFunc>) => { | ||
| record(tag, performance.now() - start); | ||
| return res; | ||
| }); | ||
| } | ||
| record(tag, performance.now() - start); | ||
| return result; |
There was a problem hiding this comment.
Can we use performance.mark and performance.measure instead of manually recording time? https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure#examples
Another added benefit is getting tracking for free: https://developer.mozilla.org/en-US/docs/Web/API/Performance/getEntries
There was a problem hiding this comment.
I'm reworking this a bit to be cleaner and will support both. The perf markers are heavier than timing based ones and have memory bloat issues. Instrumenting babel plugins with them would run out of memory. It only takes a slight adjustment to make either work.
| <!-- We recommend an empty change log entry for a new package: `yarn change --empty` --> | ||
|
|
There was a problem hiding this comment.
Remove
| <!-- We recommend an empty change log entry for a new package: `yarn change --empty` --> |
| } | ||
| return result; | ||
| }); | ||
| // use console.table for nice formatting |
There was a problem hiding this comment.
Do you plan to implement this in this PR or later?
There was a problem hiding this comment.
Old comment. Will clean it up in the next iteration.
| export const exitHandler = (() => { | ||
| let callbacks: (() => void)[] | undefined = undefined; | ||
|
|
||
| function add(callback: () => void) { | ||
| if (!callbacks) { | ||
| callbacks = []; | ||
| process.on("exit", () => { | ||
| for (const cb of callbacks!) { | ||
| cb(); | ||
| } | ||
| }); | ||
| } | ||
| callbacks.push(callback); | ||
| } |
There was a problem hiding this comment.
Avoid force nonnull assertion. Rewrite to help TypeScript infer nullability:
| export const exitHandler = (() => { | |
| let callbacks: (() => void)[] | undefined = undefined; | |
| function add(callback: () => void) { | |
| if (!callbacks) { | |
| callbacks = []; | |
| process.on("exit", () => { | |
| for (const cb of callbacks!) { | |
| cb(); | |
| } | |
| }); | |
| } | |
| callbacks.push(callback); | |
| } | |
| type OnExit = () => void; | |
| export const exitHandler = (() => { | |
| let callbacks: OnExit[] | undefined = undefined; | |
| function add(callback: OnExit) { | |
| if (!callbacks) { | |
| const arr: OnExit[] = []; | |
| process.on("exit", () => { | |
| for (const cb of arr) { | |
| cb(); | |
| } | |
| }); | |
| callbacks = arr; | |
| } | |
| callbacks.push(callback); | |
| } |
| if (callbacks) { | ||
| callbacks = callbacks.filter((cb) => cb !== callback); | ||
| } |
There was a problem hiding this comment.
Should we be using Set<OnExit> instead to ensure uniqueness and gain access to .delete()?
| } else { | ||
| for (const cat of category) { | ||
| this.enabled.add(cat); | ||
| } | ||
| } |
There was a problem hiding this comment.
We should probably also check that category is an actual array:
| } else { | |
| for (const cat of category) { | |
| this.enabled.add(cat); | |
| } | |
| } | |
| } else if (Array.isArray(category)) { | |
| for (const cat of category) { | |
| this.enabled.add(cat); | |
| } | |
| } else { | |
| throw new Error(`invalid category: ${category}`); | |
| } |
| getTrace(category?: string): TraceFunction { | ||
| return this.getCategory(category)?.trace ?? nullTrace; | ||
| } | ||
|
|
||
| getRecorder(category?: string): TraceRecorder { | ||
| return this.getCategory(category)?.record ?? nullRecord; | ||
| } |
There was a problem hiding this comment.
These should return undefined since we're already handling nullable in the module-level getRecorder/getTrace methods.
Description
This adds a framework for doing lightweight performance tracing, some basic reporting, and enabling tracing at a global level.
Usage
In packages, to add instrumentation:
Right now, to enable this for metro bundling a user would add in their metro.config.js:
Future things to add (or consider)
Other notes