Wrap performance metrics in object to reduce deoptimizations on ts namespace.

This commit is contained in:
Ron Buckton 2016-05-17 18:37:08 -07:00
parent 60e1ae0e9f
commit b8a9efb66c
5 changed files with 163 additions and 28 deletions

View File

@ -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 {

View File

@ -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.

View File

@ -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();
}
}
}

View File

@ -4,11 +4,6 @@
/// <reference path="printer.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 */
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<ResolvedTypeReferenceDirective> = {};
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)) {

View File

@ -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));