Skip to content

Add performance framework from transforms branch #9536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ if (process.env.path !== undefined) {

var compilerSources = [
"core.ts",
"performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
Expand All @@ -54,6 +55,7 @@ var compilerSources = [

var servicesSources = [
"core.ts",
"performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

/* @internal */
namespace ts {
export let bindTime = 0;

export const enum ModuleInstanceState {
NonInstantiated = 0,
Instantiated = 1,
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -17016,11 +17014,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.
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ namespace ts {
name: "diagnostics",
type: "boolean",
},
{
name: "extendedDiagnostics",
type: "boolean",
},
{
name: "emitBOM",
type: "boolean"
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/// <reference path="types.ts"/>
/// <reference path="performance.ts" />


/* @internal */
namespace ts {
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
/// <reference path="scanner.ts"/>

namespace ts {
/* @internal */ export let parseTime = 0;

let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;

Expand Down Expand Up @@ -413,10 +411,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;
}

Expand Down
107 changes: 107 additions & 0 deletions src/compiler/performance.ts
Original file line number Diff line number Diff line change
@@ -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<number>;
let measures: Map<number>;

/**
* 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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. See comment below on new Date().getTime() performance (it should be +new Date() instead here).

  2. Also please use typeof performance!=='undefined' instead of just checking for truthiness. Otherwise you'll get ReferenceError on platforms that don't have it defined.

  3. Lastly, it's quite non-obvious whether performance identifier here resolves to global-scope performance or ts.performance. I can see you've got that declare const performance up at line 6 — but it feels like a trick, and it may come back biting as resolution rules change all the time.

    Perhaps a clearer solution would be to move the whole Date.now thing in together with other fallback-safe utils (forEach, contains, indexOf) in core.ts around line 84?

    namespace ts {
        export var preciseTime =
            typeof performance!=='undefined' && performance && performance.now ? performance.now :
            Date.now ? Date.now :
            () => +new Date();
    }

    That way rather than having defined and called markInternal here in performance.ts, you'd call ts.preciseTime() from core.ts. Also you would replace overly optimistic Date.now at shims.ts isCancellationRequested at line 426 and editorServices.ts resolveNamesWithLocalCache line 145

}

/** Disables (and clears) performance measurements for the compiler. */
export function disable() {
counters = undefined;
measures = undefined;
profilerEvent = undefined;
}
}
}
21 changes: 8 additions & 13 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
/// <reference path="core.ts" />

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.0.0";

Expand Down Expand Up @@ -871,9 +866,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) {
Expand Down Expand Up @@ -940,7 +935,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) {
Expand All @@ -950,7 +945,7 @@ namespace ts {
sys.writeFile(fileName, data, writeByteOrderMark);
}

ioWriteTime += new Date().getTime() - start;
performance.measure("I/O Write", start);
}
catch (e) {
if (onError) {
Expand Down Expand Up @@ -1104,7 +1099,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<boolean> = {};

const start = new Date().getTime();
const start = performance.mark();

host = host || createCompilerHost(options);

Expand Down Expand Up @@ -1203,7 +1198,7 @@ namespace ts {

verifyCompilerOptions();

programTime += new Date().getTime() - start;
performance.measure("Program", start);

return program;

Expand Down Expand Up @@ -1446,14 +1441,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;
}

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/sourcemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ namespace ts {
return;
}

const start = performance.mark();

const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);

// Convert the location to be one-based.
Expand Down Expand Up @@ -279,6 +281,8 @@ namespace ts {
}

updateLastEncodedAndRecordedSpans();

performance.measure("Source Map", start);
}

function getStartPos(range: TextRange) {
Expand Down
41 changes: 24 additions & 17 deletions src/compiler/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,12 +554,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();
Expand All @@ -570,7 +566,7 @@ namespace ts {
});
}

if (compilerOptions.diagnostics) {
if (hasDiagnostics) {
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
reportCountStatistic("Lines", countLines(program));
Expand All @@ -583,17 +579,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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this you'll get different names for statistics than you do below (e.g. "I/O read time" vs. "I/O read").

Copy link
Member Author

@weswigham weswigham Jul 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I could just special-case out the builtins while printing and print them via the old path (though I'd honestly prefer using a single code path if renaming them is okay).

}
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 };
Expand Down
1 change: 1 addition & 0 deletions src/compiler/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"files": [
"core.ts",
"performance.ts",
"sys.ts",
"types.ts",
"scanner.ts",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2535,6 +2535,7 @@ namespace ts {
declaration?: boolean;
declarationDir?: string;
/* @internal */ diagnostics?: boolean;
/* @internal */ extendedDiagnostics?: boolean;
disableSizeLimit?: boolean;
emitBOM?: boolean;
emitDecoratorMetadata?: boolean;
Expand Down