Skip to content

Commit b8a9efb

Browse files
committed
Wrap performance metrics in object to reduce deoptimizations on ts namespace.
1 parent 60e1ae0 commit b8a9efb

5 files changed

Lines changed: 163 additions & 28 deletions

File tree

src/compiler/binder.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
/* @internal */
55
namespace ts {
6-
export let bindTime = 0;
7-
86
export const enum ModuleInstanceState {
97
NonInstantiated = 0,
108
Instantiated = 1,
@@ -96,9 +94,10 @@ namespace ts {
9694
const binder = createBinder();
9795

9896
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
99-
const start = new Date().getTime();
97+
Performance.mark("bindStart");
10098
binder(file, options);
101-
bindTime += new Date().getTime() - start;
99+
Performance.mark("bindEnd");
100+
Performance.measure("bindTime", "bindStart", "bindEnd");
102101
}
103102

104103
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {

src/compiler/checker.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ namespace ts {
1414
return node.id;
1515
}
1616

17-
export let checkTime = 0;
18-
1917
export function getSymbolId(symbol: Symbol): number {
2018
if (!symbol.id) {
2119
symbol.id = nextSymbolId;
@@ -16066,11 +16064,12 @@ namespace ts {
1606616064
}
1606716065

1606816066
function checkSourceFile(node: SourceFile) {
16069-
const start = new Date().getTime();
16067+
Performance.mark("checkStart");
1607016068

1607116069
checkSourceFileWorker(node);
1607216070

16073-
checkTime += new Date().getTime() - start;
16071+
Performance.mark("checkEnd");
16072+
Performance.measure("checkTime", "checkStart", "checkEnd");
1607416073
}
1607516074

1607616075
// Fully type check a source file and collect the relevant diagnostics.

src/compiler/core.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,4 +1134,139 @@ namespace ts {
11341134
: ((fileName) => fileName.toLowerCase());
11351135
}
11361136

1137+
/*@internal*/
1138+
export namespace Performance {
1139+
interface MarkData {
1140+
markName: string;
1141+
timestamp: number;
1142+
}
1143+
1144+
interface MeasureData {
1145+
measureName: string;
1146+
startMarkName: string;
1147+
endMarkName: string;
1148+
timestamp: number;
1149+
marksOffset: number;
1150+
}
1151+
1152+
export interface Measure {
1153+
name: string;
1154+
startTime: number;
1155+
duration: number;
1156+
}
1157+
1158+
const marks: MarkData[] = [];
1159+
const measures: MeasureData[] = [];
1160+
1161+
let start = now();
1162+
1163+
/** Gets the current timer for performance measurements. */
1164+
export function now() {
1165+
// TODO(rbuckton): Determine if there is a higher-resolution timer we can use.
1166+
return Date.now();
1167+
}
1168+
1169+
/**
1170+
* Adds a performance mark with the specified name.
1171+
*
1172+
* @param markName The name of the performance mark.
1173+
*/
1174+
export function mark(markName: string) {
1175+
marks.push({ markName, timestamp: now() });
1176+
}
1177+
1178+
/**
1179+
* Adds a performance measurement with the specified name.
1180+
*
1181+
* @param measureName The name of the performance measurement.
1182+
* @param startMarkName The name of the starting mark.
1183+
* If provided, the most recent time value of the start mark is used.
1184+
* If not specified, the value is the time that the performance service was
1185+
* initialized or the last time it was reset.
1186+
* @param endMarkName The name of the ending mark.
1187+
* If provided, the most recent time value of the end mark is used.
1188+
* If not specified, the current time is used.
1189+
*/
1190+
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
1191+
measures.push({
1192+
measureName,
1193+
startMarkName,
1194+
endMarkName,
1195+
timestamp: now(),
1196+
marksOffset: marks.length
1197+
});
1198+
}
1199+
1200+
/**
1201+
* Gets an array of performance measures.
1202+
*
1203+
* @param measureName The name of the measure.
1204+
* If provided, only measures with the provided name are returned.
1205+
* If not specified, all measures are returned since the last time the
1206+
* performance service was reset.
1207+
*/
1208+
export function getMeasures(measureName?: string) {
1209+
const result: Measure[] = [];
1210+
for (const measure of measures) {
1211+
if (measureName !== undefined && measureName !== measure.measureName) {
1212+
continue;
1213+
}
1214+
1215+
let startOffset = 0;
1216+
let startTime = start;
1217+
if (measure.startMarkName) {
1218+
const startMarkIndex = getMarkOffset(measure.startMarkName, 0, measure.marksOffset);
1219+
if (startMarkIndex >= 0) {
1220+
startOffset = startMarkIndex;
1221+
startTime = marks[startMarkIndex].timestamp;
1222+
}
1223+
}
1224+
1225+
let endTime = measure.timestamp;
1226+
if (measure.endMarkName) {
1227+
const endMarkIndex = getMarkOffset(measure.endMarkName, startOffset, measure.marksOffset);
1228+
if (endMarkIndex >= 0) {
1229+
endTime = marks[endMarkIndex].timestamp;
1230+
}
1231+
}
1232+
1233+
const duration = endTime - startTime;
1234+
result.push({
1235+
name: measure.measureName,
1236+
startTime,
1237+
duration
1238+
});
1239+
}
1240+
1241+
return result;
1242+
}
1243+
1244+
function getMarkOffset(markName: string, markStart: number, markEnd: number) {
1245+
if (markName === undefined) {
1246+
return -1;
1247+
}
1248+
1249+
if (markStart < 0) {
1250+
markStart = 0;
1251+
}
1252+
1253+
for (let i = markEnd - 1; i >= markStart; i--) {
1254+
const mark = marks[i];
1255+
if (mark.markName === markName) {
1256+
return i;
1257+
}
1258+
}
1259+
1260+
return -1;
1261+
}
1262+
1263+
/**
1264+
* Resets all marks and measurements in the performance service.
1265+
*/
1266+
export function reset() {
1267+
marks.length = 0;
1268+
measures.length = 0;
1269+
start = now();
1270+
}
1271+
}
11371272
}

src/compiler/program.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
/// <reference path="printer.ts" />
55

66
namespace ts {
7-
/* @internal */ export let programTime = 0;
8-
/* @internal */ export let emitTime = 0;
9-
/* @internal */ export let ioReadTime = 0;
10-
/* @internal */ export let ioWriteTime = 0;
11-
127
/** The version of the TypeScript compiler release */
138

149
const emptyArray: any[] = [];
@@ -781,9 +776,10 @@ namespace ts {
781776
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
782777
let text: string;
783778
try {
784-
const start = new Date().getTime();
779+
Performance.mark("ioReadStart");
785780
text = sys.readFile(fileName, options.charset);
786-
ioReadTime += new Date().getTime() - start;
781+
Performance.mark("ioReadEnd");
782+
Performance.measure("ioReadTime", "ioReadStart", "ioReadEnd");
787783
}
788784
catch (e) {
789785
if (onError) {
@@ -850,7 +846,7 @@ namespace ts {
850846

851847
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
852848
try {
853-
const start = new Date().getTime();
849+
Performance.mark("ioWriteStart");
854850
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
855851

856852
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
@@ -860,7 +856,8 @@ namespace ts {
860856
sys.writeFile(fileName, data, writeByteOrderMark);
861857
}
862858

863-
ioWriteTime += new Date().getTime() - start;
859+
Performance.mark("ioWriteEnd");
860+
Performance.measure("ioWriteTime", "ioWriteStart", "ioWriteEnd");
864861
}
865862
catch (e) {
866863
if (onError) {
@@ -962,7 +959,7 @@ namespace ts {
962959
let resolvedTypeReferenceDirectives: Map<ResolvedTypeReferenceDirective> = {};
963960
let fileProcessingDiagnostics = createDiagnosticCollection();
964961

965-
const start = new Date().getTime();
962+
Performance.mark("programStart");
966963

967964
host = host || createCompilerHost(options);
968965

@@ -1055,7 +1052,8 @@ namespace ts {
10551052

10561053
verifyCompilerOptions();
10571054

1058-
programTime += new Date().getTime() - start;
1055+
Performance.mark("programEnd");
1056+
Performance.measure("programTime", "programStart", "programEnd");
10591057

10601058
return program;
10611059

@@ -1288,7 +1286,7 @@ namespace ts {
12881286
// checked is to not pass the file to getEmitResolver.
12891287
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile);
12901288

1291-
const start = new Date().getTime();
1289+
Performance.mark("emitStart");
12921290

12931291
// TODO(rbuckton): remove USE_TRANSFORMS condition when we switch to transforms permanently.
12941292
let useLegacyEmitter = options.useLegacyEmitter;
@@ -1302,7 +1300,9 @@ namespace ts {
13021300
getEmitHost(writeFileCallback),
13031301
sourceFile);
13041302

1305-
emitTime += new Date().getTime() - start;
1303+
Performance.mark("emitEnd");
1304+
Performance.measure("emitTime", "emitStart", "emitEnd");
1305+
13061306
return emitResult;
13071307
}
13081308

@@ -2076,7 +2076,7 @@ namespace ts {
20762076
}
20772077

20782078
// Cannot specify module gen that isn't amd or system with --out
2079-
// Report this error if user specified --module moduleKind
2079+
// Report this error if user specified --module moduleKind
20802080
// or if there is external module in compilation which defaults to commonjs
20812081
const emitModuleKind = getEmitModuleKind(options);
20822082
if (outFile && (options.module || firstExternalModuleSourceFile) && !(emitModuleKind === ModuleKind.AMD || emitModuleKind === ModuleKind.System)) {

src/compiler/tsc.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -544,12 +544,7 @@ namespace ts {
544544
}
545545

546546
function compile(fileNames: string[], compilerOptions: CompilerOptions, compilerHost: CompilerHost) {
547-
ioReadTime = 0;
548-
ioWriteTime = 0;
549-
programTime = 0;
550-
bindTime = 0;
551-
checkTime = 0;
552-
emitTime = 0;
547+
Performance.reset();
553548

554549
const program = createProgram(fileNames, compilerOptions, compilerHost);
555550
const exitStatus = compileProgram();
@@ -561,6 +556,13 @@ namespace ts {
561556
}
562557

563558
if (compilerOptions.diagnostics) {
559+
const ioReadTime = reduceLeft(Performance.getMeasures("ioReadTime"), (aggregate, measure) => aggregate + measure.duration, 0);
560+
const ioWriteTime = reduceLeft(Performance.getMeasures("ioWriteTime"), (aggregate, measure) => aggregate + measure.duration, 0);
561+
const programTime = reduceLeft(Performance.getMeasures("programTime"), (aggregate, measure) => aggregate + measure.duration, 0);
562+
const bindTime = reduceLeft(Performance.getMeasures("bindTime"), (aggregate, measure) => aggregate + measure.duration, 0);
563+
const checkTime = reduceLeft(Performance.getMeasures("checkTime"), (aggregate, measure) => aggregate + measure.duration, 0);
564+
const emitTime = reduceLeft(Performance.getMeasures("emitTime"), (aggregate, measure) => aggregate + measure.duration, 0);
565+
564566
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
565567
reportCountStatistic("Files", program.getSourceFiles().length);
566568
reportCountStatistic("Lines", countLines(program));

0 commit comments

Comments
 (0)