• 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    export function forEachMark(cb: (markName: string) => void) {
111        marks.forEach((_time, markName) => cb(markName));
112    }
113
114    export function clearMeasures(name?: string) {
115        if (name !== undefined) durations.delete(name);
116        else durations.clear();
117        performanceImpl?.clearMeasures(name);
118    }
119
120    export function clearMarks(name?: string) {
121        if (name !== undefined) {
122            counts.delete(name);
123            marks.delete(name);
124        }
125        else {
126            counts.clear();
127            marks.clear();
128        }
129        performanceImpl?.clearMarks(name);
130    }
131
132    /**
133     * Indicates whether the performance API is enabled.
134     */
135    export function isEnabled() {
136        return enabled;
137    }
138
139    /** Enables (and resets) performance measurements for the compiler. */
140    export function enable(system: System = sys) {
141        if (!enabled) {
142            enabled = true;
143            perfHooks ||= tryGetNativePerformanceHooks();
144            if (perfHooks) {
145                timeorigin = perfHooks.performance.timeOrigin;
146                // NodeJS's Web Performance API is currently slower than expected, but we'd still like
147                // to be able to leverage native trace events when node is run with either `--cpu-prof`
148                // or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when
149                // running in debug mode (since its possible to generate a cpu profile while debugging).
150                if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) {
151                    performanceImpl = perfHooks.performance;
152                }
153            }
154        }
155        return true;
156    }
157
158    /** Disables performance measurements for the compiler. */
159    export function disable() {
160        if (enabled) {
161            marks.clear();
162            counts.clear();
163            durations.clear();
164            performanceImpl = undefined;
165            enabled = false;
166        }
167    }
168}
169