diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index f0c58c66bef..1619fc0fc8c 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -3,8 +3,6 @@
/* @internal */
namespace ts {
- export let bindTime = 0;
-
export const enum ModuleInstanceState {
NonInstantiated = 0,
Instantiated = 1,
@@ -96,9 +94,10 @@ namespace ts {
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
- const start = new Date().getTime();
+ Performance.mark("bindStart");
binder(file, options);
- bindTime += new Date().getTime() - start;
+ Performance.mark("bindEnd");
+ Performance.measure("bindTime", "bindStart", "bindEnd");
}
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 344dd35a5c3..725ed2c1bad 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -14,8 +14,6 @@ namespace ts {
return node.id;
}
- export let checkTime = 0;
-
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId;
@@ -16066,11 +16064,12 @@ namespace ts {
}
function checkSourceFile(node: SourceFile) {
- const start = new Date().getTime();
+ Performance.mark("checkStart");
checkSourceFileWorker(node);
- checkTime += new Date().getTime() - start;
+ Performance.mark("checkEnd");
+ Performance.measure("checkTime", "checkStart", "checkEnd");
}
// Fully type check a source file and collect the relevant diagnostics.
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index bae17c642d2..0a070844903 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1134,4 +1134,139 @@ namespace ts {
: ((fileName) => fileName.toLowerCase());
}
+ /*@internal*/
+ export namespace Performance {
+ interface MarkData {
+ markName: string;
+ timestamp: number;
+ }
+
+ interface MeasureData {
+ measureName: string;
+ startMarkName: string;
+ endMarkName: string;
+ timestamp: number;
+ marksOffset: number;
+ }
+
+ export interface Measure {
+ name: string;
+ startTime: number;
+ duration: number;
+ }
+
+ const marks: MarkData[] = [];
+ const measures: MeasureData[] = [];
+
+ let start = now();
+
+ /** Gets the current timer for performance measurements. */
+ export function now() {
+ // TODO(rbuckton): Determine if there is a higher-resolution timer we can use.
+ return Date.now();
+ }
+
+ /**
+ * Adds a performance mark with the specified name.
+ *
+ * @param markName The name of the performance mark.
+ */
+ export function mark(markName: string) {
+ marks.push({ markName, timestamp: now() });
+ }
+
+ /**
+ * 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 provided, the most recent time value of the start mark is used.
+ * If not specified, the value is the time that the performance service was
+ * initialized or the last time it was reset.
+ * @param endMarkName The name of the ending mark.
+ * If provided, the most recent time value of the end mark is used.
+ * If not specified, the current time is used.
+ */
+ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
+ measures.push({
+ measureName,
+ startMarkName,
+ endMarkName,
+ timestamp: now(),
+ marksOffset: marks.length
+ });
+ }
+
+ /**
+ * Gets an array of performance measures.
+ *
+ * @param measureName The name of the measure.
+ * If provided, only measures with the provided name are returned.
+ * If not specified, all measures are returned since the last time the
+ * performance service was reset.
+ */
+ export function getMeasures(measureName?: string) {
+ const result: Measure[] = [];
+ for (const measure of measures) {
+ if (measureName !== undefined && measureName !== measure.measureName) {
+ continue;
+ }
+
+ let startOffset = 0;
+ let startTime = start;
+ if (measure.startMarkName) {
+ const startMarkIndex = getMarkOffset(measure.startMarkName, 0, measure.marksOffset);
+ if (startMarkIndex >= 0) {
+ startOffset = startMarkIndex;
+ startTime = marks[startMarkIndex].timestamp;
+ }
+ }
+
+ let endTime = measure.timestamp;
+ if (measure.endMarkName) {
+ const endMarkIndex = getMarkOffset(measure.endMarkName, startOffset, measure.marksOffset);
+ if (endMarkIndex >= 0) {
+ endTime = marks[endMarkIndex].timestamp;
+ }
+ }
+
+ const duration = endTime - startTime;
+ result.push({
+ name: measure.measureName,
+ startTime,
+ duration
+ });
+ }
+
+ return result;
+ }
+
+ function getMarkOffset(markName: string, markStart: number, markEnd: number) {
+ if (markName === undefined) {
+ return -1;
+ }
+
+ if (markStart < 0) {
+ markStart = 0;
+ }
+
+ for (let i = markEnd - 1; i >= markStart; i--) {
+ const mark = marks[i];
+ if (mark.markName === markName) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Resets all marks and measurements in the performance service.
+ */
+ export function reset() {
+ marks.length = 0;
+ measures.length = 0;
+ start = now();
+ }
+ }
}
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index bf5b7cb5727..1e7d4118e6c 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -4,11 +4,6 @@
///
namespace ts {
- /* @internal */ export let programTime = 0;
- /* @internal */ export let emitTime = 0;
- /* @internal */ export let ioReadTime = 0;
- /* @internal */ export let ioWriteTime = 0;
-
/** The version of the TypeScript compiler release */
const emptyArray: any[] = [];
@@ -781,9 +776,10 @@ namespace ts {
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
let text: string;
try {
- const start = new Date().getTime();
+ Performance.mark("ioReadStart");
text = sys.readFile(fileName, options.charset);
- ioReadTime += new Date().getTime() - start;
+ Performance.mark("ioReadEnd");
+ Performance.measure("ioReadTime", "ioReadStart", "ioReadEnd");
}
catch (e) {
if (onError) {
@@ -850,7 +846,7 @@ namespace ts {
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
- const start = new Date().getTime();
+ Performance.mark("ioWriteStart");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
@@ -860,7 +856,8 @@ namespace ts {
sys.writeFile(fileName, data, writeByteOrderMark);
}
- ioWriteTime += new Date().getTime() - start;
+ Performance.mark("ioWriteEnd");
+ Performance.measure("ioWriteTime", "ioWriteStart", "ioWriteEnd");
}
catch (e) {
if (onError) {
@@ -962,7 +959,7 @@ namespace ts {
let resolvedTypeReferenceDirectives: Map = {};
let fileProcessingDiagnostics = createDiagnosticCollection();
- const start = new Date().getTime();
+ Performance.mark("programStart");
host = host || createCompilerHost(options);
@@ -1055,7 +1052,8 @@ namespace ts {
verifyCompilerOptions();
- programTime += new Date().getTime() - start;
+ Performance.mark("programEnd");
+ Performance.measure("programTime", "programStart", "programEnd");
return program;
@@ -1288,7 +1286,7 @@ namespace ts {
// checked is to not pass the file to getEmitResolver.
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile);
- const start = new Date().getTime();
+ Performance.mark("emitStart");
// TODO(rbuckton): remove USE_TRANSFORMS condition when we switch to transforms permanently.
let useLegacyEmitter = options.useLegacyEmitter;
@@ -1302,7 +1300,9 @@ namespace ts {
getEmitHost(writeFileCallback),
sourceFile);
- emitTime += new Date().getTime() - start;
+ Performance.mark("emitEnd");
+ Performance.measure("emitTime", "emitStart", "emitEnd");
+
return emitResult;
}
@@ -2076,7 +2076,7 @@ namespace ts {
}
// Cannot specify module gen that isn't amd or system with --out
- // Report this error if user specified --module moduleKind
+ // Report this error if user specified --module moduleKind
// or if there is external module in compilation which defaults to commonjs
const emitModuleKind = getEmitModuleKind(options);
if (outFile && (options.module || firstExternalModuleSourceFile) && !(emitModuleKind === ModuleKind.AMD || emitModuleKind === ModuleKind.System)) {
diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts
index 7c9fae6b632..2325f75a665 100644
--- a/src/compiler/tsc.ts
+++ b/src/compiler/tsc.ts
@@ -544,12 +544,7 @@ namespace ts {
}
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
- ioReadTime = 0;
- ioWriteTime = 0;
- programTime = 0;
- bindTime = 0;
- checkTime = 0;
- emitTime = 0;
+ Performance.reset();
const program = createProgram(fileNames, compilerOptions, compilerHost);
const exitStatus = compileProgram();
@@ -561,6 +556,13 @@ namespace ts {
}
if (compilerOptions.diagnostics) {
+ const ioReadTime = reduceLeft(Performance.getMeasures("ioReadTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+ const ioWriteTime = reduceLeft(Performance.getMeasures("ioWriteTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+ const programTime = reduceLeft(Performance.getMeasures("programTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+ const bindTime = reduceLeft(Performance.getMeasures("bindTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+ const checkTime = reduceLeft(Performance.getMeasures("checkTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+ const emitTime = reduceLeft(Performance.getMeasures("emitTime"), (aggregate, measure) => aggregate + measure.duration, 0);
+
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
reportCountStatistic("Lines", countLines(program));