mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Switched 'ts.performance' to a mixed mode only uses native performance APIs when necessary (#42586)
* Partially revert native performance * Fix bug in measure * Conditionally enable native perf events
This commit is contained in:
parent
66ecfcbd04
commit
c953969698
@ -2,7 +2,6 @@
|
||||
/** Performance measurements for the compiler. */
|
||||
namespace ts.performance {
|
||||
let perfHooks: PerformanceHooks | undefined;
|
||||
let perfObserver: PerformanceObserver | 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;
|
||||
@ -41,6 +40,10 @@ namespace ts.performance {
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
@ -50,7 +53,12 @@ namespace ts.performance {
|
||||
* @param markName The name of the mark.
|
||||
*/
|
||||
export function mark(markName: string) {
|
||||
performanceImpl?.mark(markName);
|
||||
if (enabled) {
|
||||
const count = counts.get(markName) ?? 0;
|
||||
counts.set(markName, count + 1);
|
||||
marks.set(markName, timestamp());
|
||||
performanceImpl?.mark(markName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +71,13 @@ namespace ts.performance {
|
||||
* used.
|
||||
*/
|
||||
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
|
||||
performanceImpl?.measure(measureName, startMarkName, endMarkName);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,35 +111,36 @@ namespace ts.performance {
|
||||
* Indicates whether the performance API is enabled.
|
||||
*/
|
||||
export function isEnabled() {
|
||||
return !!performanceImpl;
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/** Enables (and resets) performance measurements for the compiler. */
|
||||
export function enable() {
|
||||
if (!performanceImpl) {
|
||||
export function enable(system: System = sys) {
|
||||
if (!enabled) {
|
||||
enabled = true;
|
||||
perfHooks ||= tryGetNativePerformanceHooks();
|
||||
if (!perfHooks) return false;
|
||||
perfObserver ||= new perfHooks.PerformanceObserver(updateStatisticsFromList);
|
||||
perfObserver.observe({ entryTypes: ["mark", "measure"] });
|
||||
performanceImpl = perfHooks.performance;
|
||||
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;
|
||||
}
|
||||
|
||||
/** Disables performance measurements for the compiler. */
|
||||
export function disable() {
|
||||
perfObserver?.disconnect();
|
||||
performanceImpl = undefined;
|
||||
counts.clear();
|
||||
durations.clear();
|
||||
}
|
||||
|
||||
function updateStatisticsFromList(list: PerformanceObserverEntryList) {
|
||||
for (const mark of list.getEntriesByType("mark")) {
|
||||
counts.set(mark.name, (counts.get(mark.name) || 0) + 1);
|
||||
}
|
||||
for (const measure of list.getEntriesByType("measure")) {
|
||||
durations.set(measure.name, (durations.get(measure.name) || 0) + measure.duration);
|
||||
if (enabled) {
|
||||
marks.clear();
|
||||
counts.clear();
|
||||
durations.clear();
|
||||
performanceImpl = undefined;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ namespace ts {
|
||||
// between browsers and NodeJS:
|
||||
|
||||
export interface PerformanceHooks {
|
||||
/** Indicates whether we should write native performance events */
|
||||
shouldWriteNativeEvents: boolean;
|
||||
performance: Performance;
|
||||
PerformanceObserver: PerformanceObserverConstructor;
|
||||
}
|
||||
@ -37,6 +39,7 @@ namespace ts {
|
||||
export type PerformanceEntryList = PerformanceEntry[];
|
||||
|
||||
// Browser globals for the Web Performance User Timings API
|
||||
declare const process: any;
|
||||
declare const performance: Performance | undefined;
|
||||
declare const PerformanceObserver: PerformanceObserverConstructor | undefined;
|
||||
|
||||
@ -55,6 +58,10 @@ namespace ts {
|
||||
typeof PerformanceObserver === "function" &&
|
||||
hasRequiredAPI(performance, PerformanceObserver)) {
|
||||
return {
|
||||
// For now we always write native performance events when running in the browser. We may
|
||||
// make this conditional in the future if we find that native web performance hooks
|
||||
// in the browser also slow down compilation.
|
||||
shouldWriteNativeEvents: true,
|
||||
performance,
|
||||
PerformanceObserver
|
||||
};
|
||||
@ -62,10 +69,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function tryGetNodePerformanceHooks(): PerformanceHooks | undefined {
|
||||
if (typeof module === "object" && typeof require === "function") {
|
||||
if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof module === "object" && typeof require === "function") {
|
||||
try {
|
||||
const { performance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
|
||||
if (hasRequiredAPI(performance, PerformanceObserver)) {
|
||||
let performance: Performance;
|
||||
const { performance: nodePerformance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
|
||||
if (hasRequiredAPI(nodePerformance, PerformanceObserver)) {
|
||||
performance = nodePerformance;
|
||||
// There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not
|
||||
// match the Web Performance API specification. Node's implementation did not allow
|
||||
// optional `start` and `end` arguments for `performance.measure`.
|
||||
@ -73,26 +82,25 @@ namespace ts {
|
||||
const version = new Version(process.versions.node);
|
||||
const range = new VersionRange("<12.16.3 || 13 <13.13");
|
||||
if (range.test(version)) {
|
||||
return {
|
||||
performance: {
|
||||
get timeOrigin() { return performance.timeOrigin; },
|
||||
now() { return performance.now(); },
|
||||
mark(name) { return performance.mark(name); },
|
||||
measure(name, start = "nodeStart", end?) {
|
||||
if (end === undefined) {
|
||||
end = "__performance.measure-fix__";
|
||||
performance.mark(end);
|
||||
}
|
||||
performance.measure(name, start, end);
|
||||
if (end === "__performance.measure-fix__") {
|
||||
performance.clearMarks("__performance.measure-fix__");
|
||||
}
|
||||
performance = {
|
||||
get timeOrigin() { return nodePerformance.timeOrigin; },
|
||||
now() { return nodePerformance.now(); },
|
||||
mark(name) { return nodePerformance.mark(name); },
|
||||
measure(name, start = "nodeStart", end?) {
|
||||
if (end === undefined) {
|
||||
end = "__performance.measure-fix__";
|
||||
nodePerformance.mark(end);
|
||||
}
|
||||
},
|
||||
PerformanceObserver
|
||||
nodePerformance.measure(name, start, end);
|
||||
if (end === "__performance.measure-fix__") {
|
||||
nodePerformance.clearMarks("__performance.measure-fix__");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
// By default, only write native events when generating a cpu profile or using the v8 profiler.
|
||||
shouldWriteNativeEvents: false,
|
||||
performance,
|
||||
PerformanceObserver
|
||||
};
|
||||
|
||||
@ -1116,6 +1116,7 @@ namespace ts {
|
||||
exit(exitCode?: number): void;
|
||||
/*@internal*/ enableCPUProfiler?(path: string, continuation: () => void): boolean;
|
||||
/*@internal*/ disableCPUProfiler?(continuation: () => void): boolean;
|
||||
/*@internal*/ cpuProfilingEnabled?(): boolean;
|
||||
realpath?(path: string): string;
|
||||
/*@internal*/ getEnvironmentVariable(name: string): string;
|
||||
/*@internal*/ tryEnableSourceMapsForHost?(): void;
|
||||
@ -1286,6 +1287,7 @@ namespace ts {
|
||||
},
|
||||
enableCPUProfiler,
|
||||
disableCPUProfiler,
|
||||
cpuProfilingEnabled: () => !!activeSession || contains(process.execArgv, "--cpu-prof") || contains(process.execArgv, "--prof"),
|
||||
realpath,
|
||||
debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
|
||||
tryEnableSourceMapsForHost() {
|
||||
|
||||
@ -662,7 +662,7 @@ namespace ts {
|
||||
|
||||
function enableStatisticsAndTracing(system: System, compilerOptions: CompilerOptions, isBuildMode: boolean) {
|
||||
if (canReportDiagnostics(system, compilerOptions)) {
|
||||
performance.enable();
|
||||
performance.enable(system);
|
||||
}
|
||||
|
||||
if (canTrace(system, compilerOptions)) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user