• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2/** Performance measurements for the compiler. */
3namespace ts.performance {
4    let perfHooks: PerformanceHooks | undefined;
5    // when set, indicates the implementation of `Performance` to use for user timing.
6    // when unset, indicates user timing is unavailable or disabled.
7    let performanceImpl: Performance | undefined;
8
9    export interface Timer {
10        enter(): void;
11        exit(): void;
12    }
13
14    export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) {
15        return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer;
16    }
17
18    export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer {
19        let enterCount = 0;
20        return {
21            enter,
22            exit
23        };
24
25        function enter() {
26            if (++enterCount === 1) {
27                mark(startMarkName);
28            }
29        }
30
31        function exit() {
32            if (--enterCount === 0) {
33                mark(endMarkName);
34                measure(measureName, startMarkName, endMarkName);
35            }
36            else if (enterCount < 0) {
37                Debug.fail("enter/exit count does not match.");
38            }
39        }
40    }
41
42    export const nullTimer: Timer = { enter: noop, exit: noop };
43
44    let enabled = false;
45    let timeorigin = timestamp();
46    const marks = new Map<string, number>();
47    const counts = new Map<string, number>();
48    const durations = new Map<string, number>();
49
50    /**
51     * Marks a performance event.
52     *
53     * @param markName The name of the mark.
54     */
55    export function mark(markName: string) {
56        if (enabled) {
57            const count = counts.get(markName) ?? 0;
58            counts.set(markName, count + 1);
59            marks.set(markName, timestamp());
60            performanceImpl?.mark(markName);
61        }
62    }
63
64    /**
65     * Adds a performance measurement with the specified name.
66     *
67     * @param measureName The name of the performance measurement.
68     * @param startMarkName The name of the starting mark. If not supplied, the point at which the
69     *      profiler was enabled is used.
70     * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is
71     *      used.
72     */
73    export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
74        if (enabled) {
75            const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp();
76            const start = (startMarkName !== undefined ? marks.get(startMarkName) : undefined) ?? timeorigin;
77            const previousDuration = durations.get(measureName) || 0;
78            durations.set(measureName, previousDuration + (end - start));
79            performanceImpl?.measure(measureName, startMarkName, endMarkName);
80        }
81    }
82
83    /**
84     * Gets the number of times a marker was encountered.
85     *
86     * @param markName The name of the mark.
87     */
88    export function getCount(markName: string) {
89        return counts.get(markName) || 0;
90    }
91
92    /**
93     * Gets the total duration of all measurements with the supplied name.
94     *
95     * @param measureName The name of the measure whose durations should be accumulated.
96     */
97    export function getDuration(measureName: string) {
98        return durations.get(measureName) || 0;
99    }
100
101    /**
102     * Iterate over each measure, performing some action
103     *
104     * @param cb The action to perform for each measure
105     */
106    export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
107        durations.forEach((duration, measureName) => cb(measureName, duration));
108    }
109
110    /**
111     * Indicates whether the performance API is enabled.
112     */
113    export function isEnabled() {
114        return enabled;
115    }
116
117    /** Enables (and resets) performance measurements for the compiler. */
118    export function enable(system: System = sys) {
119        if (!enabled) {
120            enabled = true;
121            perfHooks ||= tryGetNativePerformanceHooks();
122            if (perfHooks) {
123                timeorigin = perfHooks.performance.timeOrigin;
124                // NodeJS's Web Performance API is currently slower than expected, but we'd still like
125                // to be able to leverage native trace events when node is run with either `--cpu-prof`
126                // or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when
127                // running in debug mode (since its possible to generate a cpu profile while debugging).
128                if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) {
129                    performanceImpl = perfHooks.performance;
130                }
131            }
132        }
133        return true;
134    }
135
136    /** Disables performance measurements for the compiler. */
137    export function disable() {
138        if (enabled) {
139            marks.clear();
140            counts.clear();
141            durations.clear();
142            performanceImpl = undefined;
143            enabled = false;
144        }
145    }
146}
147