diff --git a/Jakefile.js b/Jakefile.js
index eee9fe1370e..1c22305cd86 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -34,6 +34,7 @@ if (process.env.path !== undefined) {
var compilerSources = [
"core.ts",
+ "performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
@@ -54,6 +55,7 @@ var compilerSources = [
var servicesSources = [
"core.ts",
+ "performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index b7852a64d04..6059cd1f86a 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,
@@ -91,9 +89,9 @@ namespace ts {
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
- const start = new Date().getTime();
+ const start = performance.mark();
binder(file, options);
- bindTime += new Date().getTime() - start;
+ performance.measure("Bind", start);
}
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index d68502dc8e8..d769cc4e3fc 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15,8 +15,6 @@ namespace ts {
return node.id;
}
- export let checkTime = 0;
-
export function getSymbolId(symbol: Symbol): number {
if (!symbol.id) {
symbol.id = nextSymbolId;
@@ -17026,11 +17024,11 @@ namespace ts {
}
function checkSourceFile(node: SourceFile) {
- const start = new Date().getTime();
+ const start = performance.mark();
checkSourceFileWorker(node);
- checkTime += new Date().getTime() - start;
+ performance.measure("Check", start);
}
// Fully type check a source file and collect the relevant diagnostics.
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index f11854a6858..b82cf6a094d 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -27,6 +27,10 @@ namespace ts {
name: "diagnostics",
type: "boolean",
},
+ {
+ name: "extendedDiagnostics",
+ type: "boolean",
+ },
{
name: "emitBOM",
type: "boolean"
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index bffd3179e83..926ee38a795 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1,4 +1,6 @@
///
+///
+
/* @internal */
namespace ts {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 2eae2c4025e..774cea60e09 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -2,8 +2,6 @@
///
namespace ts {
- /* @internal */ export let parseTime = 0;
-
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
@@ -421,10 +419,10 @@ namespace ts {
}
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
- const start = new Date().getTime();
+ const start = performance.mark();
const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
- parseTime += new Date().getTime() - start;
+ performance.measure("Parse", start);
return result;
}
diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts
new file mode 100644
index 00000000000..f40b191a1a5
--- /dev/null
+++ b/src/compiler/performance.ts
@@ -0,0 +1,107 @@
+/*@internal*/
+namespace ts {
+ /** Performance measurements for the compiler. */
+ export namespace performance {
+ declare const onProfilerEvent: { (markName: string): void; profiler: boolean; };
+ declare const performance: { now?(): number } | undefined;
+ let profilerEvent: (markName: string) => void;
+ let markInternal: () => number;
+ let counters: Map;
+ let measures: Map;
+
+ /**
+ * Emit a performance event if ts-profiler is connected. This is primarily used
+ * to generate heap snapshots.
+ *
+ * @param eventName A name for the event.
+ */
+ export function emit(eventName: string) {
+ if (profilerEvent) {
+ profilerEvent(eventName);
+ }
+ }
+
+ /**
+ * Increments a counter with the specified name.
+ *
+ * @param counterName The name of the counter.
+ */
+ export function increment(counterName: string) {
+ if (counters) {
+ counters[counterName] = (getProperty(counters, counterName) || 0) + 1;
+ }
+ }
+
+ /**
+ * Gets the value of the counter with the specified name.
+ *
+ * @param counterName The name of the counter.
+ */
+ export function getCount(counterName: string) {
+ return counters && getProperty(counters, counterName) || 0;
+ }
+
+ /**
+ * Marks the start of a performance measurement.
+ */
+ export function mark() {
+ return measures ? markInternal() : 0;
+ }
+
+ /**
+ * Adds a performance measurement with the specified name.
+ *
+ * @param measureName The name of the performance measurement.
+ * @param marker The timestamp of the starting mark.
+ */
+ export function measure(measureName: string, marker: number) {
+ if (measures) {
+ measures[measureName] = (getProperty(measures, measureName) || 0) + (Date.now() - marker);
+ }
+ }
+
+ /**
+ * Iterate over each measure, performing some action
+ *
+ * @param cb The action to perform for each measure
+ */
+ export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
+ return forEachKey(measures, key => cb(key, measures[key]));
+ }
+
+ /**
+ * Gets the total duration of all measurements with the supplied name.
+ *
+ * @param measureName The name of the measure whose durations should be accumulated.
+ */
+ export function getDuration(measureName: string) {
+ return measures && getProperty(measures, measureName) || 0;
+ }
+
+ /** Enables (and resets) performance measurements for the compiler. */
+ export function enable() {
+ counters = { };
+ measures = {
+ "I/O Read": 0,
+ "I/O Write": 0,
+ "Program": 0,
+ "Parse": 0,
+ "Bind": 0,
+ "Check": 0,
+ "Emit": 0,
+ };
+
+ profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true
+ ? onProfilerEvent
+ : undefined;
+ markInternal = performance && performance.now ? performance.now : Date.now ? Date.now : () => new Date().getTime();
+ }
+
+ /** Disables (and clears) performance measurements for the compiler. */
+ export function disable() {
+ counters = undefined;
+ measures = undefined;
+ profilerEvent = undefined;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 206311e2f18..988714be2ab 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -3,11 +3,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 */
export const version = "2.1.0";
@@ -865,9 +860,9 @@ namespace ts {
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
let text: string;
try {
- const start = new Date().getTime();
+ const start = performance.mark();
text = sys.readFile(fileName, options.charset);
- ioReadTime += new Date().getTime() - start;
+ performance.measure("I/O Read", start);
}
catch (e) {
if (onError) {
@@ -934,7 +929,7 @@ namespace ts {
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
- const start = new Date().getTime();
+ const start = performance.mark();
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
@@ -944,7 +939,7 @@ namespace ts {
sys.writeFile(fileName, data, writeByteOrderMark);
}
- ioWriteTime += new Date().getTime() - start;
+ performance.measure("I/O Write", start);
}
catch (e) {
if (onError) {
@@ -1121,7 +1116,7 @@ namespace ts {
// Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
const sourceFilesFoundSearchingNodeModules: Map = {};
- const start = new Date().getTime();
+ const start = performance.mark();
host = host || createCompilerHost(options);
@@ -1220,7 +1215,7 @@ namespace ts {
verifyCompilerOptions();
- programTime += new Date().getTime() - start;
+ performance.measure("Program", start);
return program;
@@ -1463,14 +1458,14 @@ 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();
+ const start = performance.mark();
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
- emitTime += new Date().getTime() - start;
+ performance.measure("Emit", start);
return emitResult;
}
diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts
index cf7c3d1eaae..2d8c36a3d02 100644
--- a/src/compiler/sourcemap.ts
+++ b/src/compiler/sourcemap.ts
@@ -240,6 +240,8 @@ namespace ts {
return;
}
+ const start = performance.mark();
+
const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);
// Convert the location to be one-based.
@@ -279,6 +281,8 @@ namespace ts {
}
updateLastEncodedAndRecordedSpans();
+
+ performance.measure("Source Map", start);
}
function getStartPos(range: TextRange) {
diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts
index 242d24caf3c..10538d0c009 100644
--- a/src/compiler/tsc.ts
+++ b/src/compiler/tsc.ts
@@ -550,12 +550,8 @@ namespace ts {
}
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
- ioReadTime = 0;
- ioWriteTime = 0;
- programTime = 0;
- bindTime = 0;
- checkTime = 0;
- emitTime = 0;
+ const hasDiagnostics = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
+ if (hasDiagnostics) performance.enable();
const program = createProgram(fileNames, compilerOptions, compilerHost);
const exitStatus = compileProgram();
@@ -566,7 +562,7 @@ namespace ts {
});
}
- if (compilerOptions.diagnostics) {
+ if (hasDiagnostics) {
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
reportCountStatistic("Lines", countLines(program));
@@ -579,17 +575,28 @@ namespace ts {
reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K");
}
- // Individual component times.
- // Note: To match the behavior of previous versions of the compiler, the reported parse time includes
- // I/O read time and processing time for triple-slash references and module imports, and the reported
- // emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
- reportTimeStatistic("I/O read", ioReadTime);
- reportTimeStatistic("I/O write", ioWriteTime);
- reportTimeStatistic("Parse time", programTime);
- reportTimeStatistic("Bind time", bindTime);
- reportTimeStatistic("Check time", checkTime);
- reportTimeStatistic("Emit time", emitTime);
+ const programTime = performance.getDuration("Program");
+ const bindTime = performance.getDuration("Bind");
+ const checkTime = performance.getDuration("Check");
+ const emitTime = performance.getDuration("Emit");
+ if (compilerOptions.extendedDiagnostics) {
+ performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration));
+ }
+ else {
+ // Individual component times.
+ // Note: To match the behavior of previous versions of the compiler, the reported parse time includes
+ // I/O read time and processing time for triple-slash references and module imports, and the reported
+ // emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
+ reportTimeStatistic("I/O read", performance.getDuration("I/O Read"));
+ reportTimeStatistic("I/O write", performance.getDuration("I/O Write"));
+ reportTimeStatistic("Parse time", programTime);
+ reportTimeStatistic("Bind time", bindTime);
+ reportTimeStatistic("Check time", checkTime);
+ reportTimeStatistic("Emit time", emitTime);
+ }
reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime);
+
+ performance.disable();
}
return { program, exitStatus };
diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json
index 827a9b81c4d..cc9bfddcece 100644
--- a/src/compiler/tsconfig.json
+++ b/src/compiler/tsconfig.json
@@ -12,6 +12,7 @@
},
"files": [
"core.ts",
+ "performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index d0df5e86550..dc332b24567 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2539,6 +2539,7 @@ namespace ts {
declaration?: boolean;
declarationDir?: string;
/* @internal */ diagnostics?: boolean;
+ /* @internal */ extendedDiagnostics?: boolean;
disableSizeLimit?: boolean;
emitBOM?: boolean;
emitDecoratorMetadata?: boolean;