From 3c637400da679883f720894e16c5625b9668f932 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:55:18 -0700 Subject: [PATCH] Use performance.now when possible, re-disable unconditional perf marking on all Node versions (#57875) --- src/compiler/performance.ts | 2 +- src/compiler/performanceCore.ts | 136 ++++++++++++++------------------ 2 files changed, 59 insertions(+), 79 deletions(-) diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 1605fa5e356..40b333041e6 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -180,7 +180,7 @@ export function enable(system: System = sys) { if (!enabled) { enabled = true; perfHooks ||= tryGetNativePerformanceHooks(); - if (perfHooks) { + if (perfHooks?.performance) { 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` diff --git a/src/compiler/performanceCore.ts b/src/compiler/performanceCore.ts index a214ced47ef..e788836d118 100644 --- a/src/compiler/performanceCore.ts +++ b/src/compiler/performanceCore.ts @@ -7,104 +7,86 @@ import { /** @internal */ export interface PerformanceHooks { - /** Indicates whether we should write native performance events */ shouldWriteNativeEvents: boolean; - performance: Performance; - PerformanceObserver: PerformanceObserverConstructor; + performance?: Performance; + performanceTime?: PerformanceTime; } /** @internal */ -export interface Performance { - mark(name: string): void; - measure(name: string, startMark?: string, endMark?: string): void; - clearMeasures(name?: string): void; - clearMarks(name?: string): void; +export interface PerformanceTime { now(): number; timeOrigin: number; } /** @internal */ -export interface PerformanceEntry { - name: string; - entryType: string; - startTime: number; - duration: number; +export interface Performance extends PerformanceTime { + mark(name: string): void; + measure(name: string, startMark?: string, endMark?: string): void; + clearMeasures(name?: string): void; + clearMarks(name?: string): void; } -/** @internal */ -export interface PerformanceObserverEntryList { - getEntries(): PerformanceEntryList; - getEntriesByName(name: string, type?: string): PerformanceEntryList; - getEntriesByType(type: string): PerformanceEntryList; -} - -/** @internal */ -export interface PerformanceObserver { - disconnect(): void; - observe(options: { entryTypes: readonly ("mark" | "measure")[]; }): void; -} - -/** @internal */ -export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver; -/** @internal */ -export type PerformanceEntryList = PerformanceEntry[]; - // Browser globals for the Web Performance User Timings API declare const performance: Performance | undefined; -declare const PerformanceObserver: PerformanceObserverConstructor | undefined; -// eslint-disable-next-line @typescript-eslint/naming-convention -function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) { - return typeof performance === "object" && - typeof performance.timeOrigin === "number" && - typeof performance.mark === "function" && - typeof performance.measure === "function" && - typeof performance.now === "function" && - typeof performance.clearMarks === "function" && - typeof performance.clearMeasures === "function" && - typeof PerformanceObserver === "function"; -} - -function tryGetWebPerformanceHooks(): PerformanceHooks | undefined { - if ( - typeof performance === "object" && - 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, - }; - } -} - -function tryGetNodePerformanceHooks(): PerformanceHooks | undefined { +function tryGetPerformance() { if (isNodeLikeSystem()) { try { - const { performance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks"); - if (hasRequiredAPI(performance, PerformanceObserver)) { - return { - // By default, only write native events when generating a cpu profile or using the v8 profiler. - shouldWriteNativeEvents: false, - performance, - PerformanceObserver, - }; - } + // By default, only write native events when generating a cpu profile or using the v8 profiler. + const { performance } = require("perf_hooks") as typeof import("perf_hooks"); + return { + shouldWriteNativeEvents: false, + performance, + }; } catch { // ignore errors } } + + if (typeof performance === "object") { + // 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. + return { + shouldWriteNativeEvents: true, + performance, + }; + } + + return undefined; } -// Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these -// since we will need them for `timestamp`, below. -const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks(); -const nativePerformance = nativePerformanceHooks?.performance; +function tryGetPerformanceHooks(): PerformanceHooks | undefined { + const p = tryGetPerformance(); + if (!p) return undefined; + const { shouldWriteNativeEvents, performance } = p; + + const hooks: PerformanceHooks = { + shouldWriteNativeEvents, + performance: undefined, + performanceTime: undefined, + }; + + if (typeof performance.timeOrigin === "number" && typeof performance.now === "function") { + hooks.performanceTime = performance; + } + + if ( + hooks.performanceTime && + typeof performance.mark === "function" && + typeof performance.measure === "function" && + typeof performance.clearMarks === "function" && + typeof performance.clearMeasures === "function" + ) { + hooks.performance = performance; + } + + return hooks; +} + +const nativePerformanceHooks = tryGetPerformanceHooks(); +const nativePerformanceTime = nativePerformanceHooks?.performanceTime; /** @internal */ export function tryGetNativePerformanceHooks() { @@ -116,6 +98,4 @@ export function tryGetNativePerformanceHooks() { * * @internal */ -export const timestamp = nativePerformance ? () => nativePerformance.now() : - Date.now ? Date.now : - () => +(new Date()); +export const timestamp = nativePerformanceTime ? () => nativePerformanceTime.now() : Date.now;