-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Report aggregate statistics for solution as well as some solution perf numbers #49285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2726b29
a7be227
d0fe796
6fc09f8
e6f8219
4daeb87
1e64835
fa213c8
53216fe
c43eb55
cdf2962
150c981
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,146 +1,209 @@ | ||
/*@internal*/ | ||
/** Performance measurements for the compiler. */ | ||
namespace ts.performance { | ||
let perfHooks: PerformanceHooks | undefined; | ||
// when set, indicates the implementation of `Performance` to use for user timing. | ||
// when unset, indicates user timing is unavailable or disabled. | ||
let performanceImpl: Performance | undefined; | ||
|
||
export interface Timer { | ||
namespace ts { | ||
interface Timer { | ||
enter(): void; | ||
exit(): void; | ||
} | ||
|
||
export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) { | ||
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer; | ||
export interface Statistic { | ||
name: string; | ||
value: number; | ||
type: StatisticType | ||
} | ||
|
||
export enum StatisticType { | ||
time, | ||
count, | ||
memory, | ||
Comment on lines
+9
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not clear on what this is for, given that we already have a concept of "count" (i.e., "marks"), and "time" (i.e., "measure"). Memory usage isn't generally tracked by the native User Timings API but can be accomplished by providing additional metadata to a mark. We could probably extend our version of |
||
} | ||
|
||
export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer { | ||
let enterCount = 0; | ||
const nullTimer: Timer = { enter: noop, exit: noop }; | ||
export const performance = createPerformanceTracker(); | ||
export const buildPerformance = createPerformanceTracker(); | ||
Comment on lines
+22
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we need two different performance instances? Do these two overlap WRT mark and measure names? The original |
||
|
||
function createPerformanceTracker() { | ||
let perfHooks: PerformanceHooks | undefined; | ||
// when set, indicates the implementation of `Performance` to use for user timing. | ||
// when unset, indicates user timing is unavailable or disabled. | ||
let performanceImpl: Performance | undefined; | ||
let enabled = false; | ||
let timeorigin = timestamp(); | ||
const marks = new Map<string, number>(); | ||
const counts = new Map<string, number>(); | ||
const durations = new Map<string, number>(); | ||
const durationMarks = new Set<string>(); | ||
let statistics: ESMap<string, Statistic> | undefined; | ||
|
||
return { | ||
enter, | ||
exit | ||
createTimerIf, | ||
createTimer, | ||
mark, | ||
measure, | ||
addStatistics, | ||
getCount, | ||
getDuration, | ||
forEachMeasure, | ||
forEachCount, | ||
forEachStatistics, | ||
isEnabled, | ||
enable, | ||
disable, | ||
}; | ||
|
||
function enter() { | ||
if (++enterCount === 1) { | ||
mark(startMarkName); | ||
function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) { | ||
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer; | ||
} | ||
|
||
function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer { | ||
let enterCount = 0; | ||
return { | ||
enter, | ||
exit | ||
}; | ||
|
||
function enter() { | ||
if (++enterCount === 1) { | ||
mark(startMarkName); | ||
} | ||
} | ||
|
||
function exit() { | ||
if (--enterCount === 0) { | ||
mark(endMarkName); | ||
measure(measureName, startMarkName, endMarkName); | ||
} | ||
else if (enterCount < 0) { | ||
Debug.fail("enter/exit count does not match."); | ||
} | ||
} | ||
} | ||
|
||
function exit() { | ||
if (--enterCount === 0) { | ||
mark(endMarkName); | ||
measure(measureName, startMarkName, endMarkName); | ||
/** | ||
* Marks a performance event. | ||
* | ||
* @param markName The name of the mark. | ||
*/ | ||
function mark(markName: string) { | ||
if (enabled) { | ||
const count = counts.get(markName) ?? 0; | ||
counts.set(markName, count + 1); | ||
marks.set(markName, timestamp()); | ||
performanceImpl?.mark(markName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This calls out to the native |
||
} | ||
else if (enterCount < 0) { | ||
Debug.fail("enter/exit count does not match."); | ||
} | ||
|
||
/** | ||
* Adds a performance measurement with the specified name. | ||
* | ||
* @param measureName The name of the performance measurement. | ||
* @param startMarkName The name of the starting mark. If not supplied, the point at which the | ||
* profiler was enabled is used. | ||
* @param endMarkName The name of the ending mark. If not supplied, the current timestamp is | ||
* used. | ||
*/ | ||
function measure(measureName: string, startMarkName: string, endMarkName: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This signature no longer aligns with the native User Timings API. Neither a |
||
if (enabled) { | ||
durationMarks.add(startMarkName).add(endMarkName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are these excluded here? They wouldn't be excluded by the native User Timings API. |
||
const end = marks.get(endMarkName) ?? timestamp(); | ||
const start = marks.get(startMarkName) ?? timeorigin; | ||
const previousDuration = durations.get(measureName) || 0; | ||
durations.set(measureName, previousDuration + (end - start)); | ||
performanceImpl?.measure(measureName, startMarkName, endMarkName); | ||
} | ||
} | ||
|
||
function addStatistics(s: Statistic) { | ||
if (enabled) { | ||
const existing = statistics?.get(s.name); | ||
if (existing) { | ||
if (existing.type === StatisticType.memory) existing.value = Math.max(existing.value, s.value); | ||
else existing.value += s.value; | ||
} | ||
else { | ||
(statistics ??= new Map()).set(s.name, s); | ||
} | ||
} | ||
} | ||
} | ||
|
||
export const nullTimer: Timer = { enter: noop, exit: noop }; | ||
|
||
let enabled = false; | ||
let timeorigin = timestamp(); | ||
const marks = new Map<string, number>(); | ||
const counts = new Map<string, number>(); | ||
const durations = new Map<string, number>(); | ||
|
||
/** | ||
* Marks a performance event. | ||
* | ||
* @param markName The name of the mark. | ||
*/ | ||
export function mark(markName: string) { | ||
if (enabled) { | ||
const count = counts.get(markName) ?? 0; | ||
counts.set(markName, count + 1); | ||
marks.set(markName, timestamp()); | ||
performanceImpl?.mark(markName); | ||
/** | ||
* Gets the number of times a marker was encountered. | ||
* | ||
* @param markName The name of the mark. | ||
*/ | ||
function getCount(markName: string) { | ||
return counts.get(markName) || 0; | ||
} | ||
} | ||
|
||
/** | ||
* Adds a performance measurement with the specified name. | ||
* | ||
* @param measureName The name of the performance measurement. | ||
* @param startMarkName The name of the starting mark. If not supplied, the point at which the | ||
* profiler was enabled is used. | ||
* @param endMarkName The name of the ending mark. If not supplied, the current timestamp is | ||
* used. | ||
*/ | ||
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { | ||
if (enabled) { | ||
const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp(); | ||
const start = (startMarkName !== undefined ? marks.get(startMarkName) : undefined) ?? timeorigin; | ||
const previousDuration = durations.get(measureName) || 0; | ||
durations.set(measureName, previousDuration + (end - start)); | ||
performanceImpl?.measure(measureName, startMarkName, endMarkName); | ||
/** | ||
* Gets the total duration of all measurements with the supplied name. | ||
* | ||
* @param measureName The name of the measure whose durations should be accumulated. | ||
*/ | ||
function getDuration(measureName: string) { | ||
return durations.get(measureName) || 0; | ||
} | ||
} | ||
|
||
/** | ||
* Gets the number of times a marker was encountered. | ||
* | ||
* @param markName The name of the mark. | ||
*/ | ||
export function getCount(markName: string) { | ||
return counts.get(markName) || 0; | ||
} | ||
/** | ||
* Iterate over each measure, performing some action | ||
* | ||
* @param cb The action to perform for each measure | ||
*/ | ||
function forEachMeasure(cb: (duration: number, measureName: string) => void) { | ||
durations.forEach(cb); | ||
} | ||
|
||
/** | ||
* Gets the total duration of all measurements with the supplied name. | ||
* | ||
* @param measureName The name of the measure whose durations should be accumulated. | ||
*/ | ||
export function getDuration(measureName: string) { | ||
return durations.get(measureName) || 0; | ||
} | ||
/** | ||
* Iterate over each count which is not duration mark, performing some action | ||
* | ||
* @param cb The action to perform for each measure | ||
*/ | ||
function forEachCount(cb: (count: number, countName: string) => void) { | ||
counts.forEach((count, countName) => !durationMarks.has(countName) && cb(count, countName)); | ||
} | ||
|
||
/** | ||
* Iterate over each measure, performing some action | ||
* | ||
* @param cb The action to perform for each measure | ||
*/ | ||
export function forEachMeasure(cb: (measureName: string, duration: number) => void) { | ||
durations.forEach((duration, measureName) => cb(measureName, duration)); | ||
} | ||
|
||
/** | ||
* Indicates whether the performance API is enabled. | ||
*/ | ||
export function isEnabled() { | ||
return enabled; | ||
} | ||
function forEachStatistics(cb: (statistic: Statistic, name: string) => void) { | ||
statistics?.forEach(cb); | ||
} | ||
|
||
/** Enables (and resets) performance measurements for the compiler. */ | ||
export function enable(system: System = sys) { | ||
if (!enabled) { | ||
enabled = true; | ||
perfHooks ||= tryGetNativePerformanceHooks(); | ||
if (perfHooks) { | ||
timeorigin = perfHooks.performance.timeOrigin; | ||
// NodeJS's Web Performance API is currently slower than expected, but we'd still like | ||
// to be able to leverage native trace events when node is run with either `--cpu-prof` | ||
// or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when | ||
// running in debug mode (since its possible to generate a cpu profile while debugging). | ||
if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) { | ||
performanceImpl = perfHooks.performance; | ||
/** | ||
* Indicates whether the performance API is enabled. | ||
*/ | ||
function isEnabled() { | ||
return enabled; | ||
} | ||
|
||
/** Enables (and resets) performance measurements for the compiler. */ | ||
function enable(system: System = sys) { | ||
if (!enabled) { | ||
enabled = true; | ||
perfHooks ||= tryGetNativePerformanceHooks(); | ||
if (perfHooks) { | ||
timeorigin = perfHooks.performance.timeOrigin; | ||
// NodeJS's Web Performance API is currently slower than expected, but we'd still like | ||
// to be able to leverage native trace events when node is run with either `--cpu-prof` | ||
// or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when | ||
// running in debug mode (since its possible to generate a cpu profile while debugging). | ||
if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) { | ||
performanceImpl = perfHooks.performance; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
return true; | ||
} | ||
|
||
/** Disables performance measurements for the compiler. */ | ||
export function disable() { | ||
if (enabled) { | ||
marks.clear(); | ||
counts.clear(); | ||
durations.clear(); | ||
performanceImpl = undefined; | ||
enabled = false; | ||
/** Disables performance measurements for the compiler. */ | ||
function disable() { | ||
if (enabled) { | ||
marks.clear(); | ||
counts.clear(); | ||
durations.clear(); | ||
durationMarks.clear(); | ||
statistics?.clear(); | ||
performanceImpl = undefined; | ||
enabled = false; | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.