From eca2788bcb258818415dcb5ea8d491962ddc4822 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 3 Dec 2019 16:59:24 -0800 Subject: [PATCH 01/10] List top 10 chooseOverload call sites --- src/compiler/checker.ts | 9 +++++++++ src/tsc/executeCommandLine.ts | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a1df2aa8cb52..b11edc6dba98b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24155,11 +24155,20 @@ namespace ts { // Whether the call is an error is determined by assignability of the arguments. The subtype pass // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. + + const callSiteId = `${ts.getSourceFileOfNode(node).path} [${node.pos}, ${node.end})`; + if (candidates.length > 1) { + performance.mark("beforeChooseOverloadSubtype"); result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); + performance.mark("afterChooseOverloadSubtype"); + performance.measure("chooseOverload - subtype - " + callSiteId, "beforeChooseOverloadSubtype", "afterChooseOverloadSubtype"); } if (!result) { + performance.mark("beforeChooseOverloadAssignable"); result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); + performance.mark("afterChooseOverloadAssignable"); + performance.measure("chooseOverload - assignable - " + callSiteId, "beforeChooseOverloadAssignable", "afterChooseOverloadAssignable"); } if (result) { return result; diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index d730de4a981d3..5682e2763395b 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -621,9 +621,11 @@ namespace ts { function reportStatistics(sys: System, program: Program) { let statistics: Statistic[]; + let overloadStatistics: {name: string, timeMs: number}[]; const compilerOptions = program.getCompilerOptions(); if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; + overloadStatistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; reportCountStatistic("Files", program.getSourceFiles().length); reportCountStatistic("Lines", countLines(program)); @@ -681,6 +683,12 @@ namespace ts { for (const { name, value } of statistics) { sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine); } + + const sortedOverloadStatistics = overloadStatistics.sort((a, b) => b.timeMs - a.timeMs); + for (let i = 0; i < 10 && i < sortedOverloadStatistics.length; i++) { + const stat = sortedOverloadStatistics[i]; + sys.write(`${stat.name}: ${stat.timeMs}ms${sys.newLine}`); + } } function reportStatisticalValue(name: string, value: string) { @@ -691,8 +699,12 @@ namespace ts { reportStatisticalValue(name, "" + count); } - function reportTimeStatistic(name: string, time: number) { - reportStatisticalValue(name, (time / 1000).toFixed(2) + "s"); + function reportTimeStatistic(name: string, timeMs: number) { + if (name.indexOf("chooseOverload") >= 0) { + overloadStatistics.push({ name, timeMs }); + return; + } + reportStatisticalValue(name, (timeMs / 1000).toFixed(2) + "s"); } } From b85b172b8e4c7b03b24c7e1ccb381791061ccedd Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 3 Dec 2019 19:47:52 -0800 Subject: [PATCH 02/10] Group by called symbol --- src/compiler/checker.ts | 31 +++++++++++++++++++++++++++---- src/compiler/performance.ts | 26 ++++++++++++++++++++++++++ src/tsc/executeCommandLine.ts | 28 ++++++++++++++++------------ 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b11edc6dba98b..b0678656ba15d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24156,19 +24156,29 @@ namespace ts { // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. - const callSiteId = `${ts.getSourceFileOfNode(node).path} [${node.pos}, ${node.end})`; - if (candidates.length > 1) { performance.mark("beforeChooseOverloadSubtype"); result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadSubtype"); - performance.measure("chooseOverload - subtype - " + callSiteId, "beforeChooseOverloadSubtype", "afterChooseOverloadSubtype"); + performance.measureOverload("beforeChooseOverloadSubtype", "afterChooseOverloadSubtype", { + kind: "subtype", + nodePos: nodePosToString(node), + candidateCount: candidates.length, + symbolName: getSymbolName(result || candidates[0]), + succeeded: !!result, + }); } if (!result) { performance.mark("beforeChooseOverloadAssignable"); result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadAssignable"); - performance.measure("chooseOverload - assignable - " + callSiteId, "beforeChooseOverloadAssignable", "afterChooseOverloadAssignable"); + performance.measureOverload("beforeChooseOverloadAssignable", "afterChooseOverloadAssignable", { + kind: "assignment", + nodePos: nodePosToString(node), + candidateCount: candidates.length, + symbolName: getSymbolName(result || candidates[0]), + succeeded: !!result, + }); } if (result) { return result; @@ -24260,6 +24270,19 @@ namespace ts { return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); + function getSymbolName(signature: Signature): string { + const decl = signature.declaration; + const symbol = decl && getSymbolOfNode(decl); + return symbol ? getSymbolNameRecursive(symbol) : "_Unknown_"; + } + + function getSymbolNameRecursive(symbol: Symbol): string { + const name = unescapeLeadingUnderscores(symbol.escapedName); + return symbol.parent + ? (getSymbolNameRecursive(symbol.parent) || "_Unknown_") + "." + name + : name; + } + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { candidatesForArgumentError = undefined; candidateForArgumentArityError = undefined; diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 3471ecd05910d..945be1342dcb2 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -18,6 +18,19 @@ namespace ts.performance { let counts: Map; let marks: Map; let measures: Map; + let overloadMeasures: ChooseOverloadMeasure[]; + + export interface ChooseOverloadStats { + kind: "subtype" | "assignment", + candidateCount: number, + nodePos: string, + symbolName: string, + succeeded: boolean, + } + + export interface ChooseOverloadMeasure extends ChooseOverloadStats { + timeMs: number + } export interface Timer { enter(): void; @@ -84,6 +97,14 @@ namespace ts.performance { } } + export function measureOverload(startMarkName: string, endMarkName: string, stats: ChooseOverloadStats) { + if (enabled) { + const end = marks.get(endMarkName)!; + const start = marks.get(startMarkName)!; + overloadMeasures.push({ ...stats, timeMs: (end - start) }); + } + } + /** * Gets the number of times a marker was encountered. * @@ -113,11 +134,16 @@ namespace ts.performance { }); } + export function getOverloadMeasures(): ReadonlyArray { + return overloadMeasures; + } + /** Enables (and resets) performance measurements for the compiler. */ export function enable() { counts = createMap(); marks = createMap(); measures = createMap(); + overloadMeasures = []; enabled = true; profilerStart = timestamp(); } diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index 5682e2763395b..c98a8328e7d98 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -621,11 +621,9 @@ namespace ts { function reportStatistics(sys: System, program: Program) { let statistics: Statistic[]; - let overloadStatistics: {name: string, timeMs: number}[]; const compilerOptions = program.getCompilerOptions(); if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; - overloadStatistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; reportCountStatistic("Files", program.getSourceFiles().length); reportCountStatistic("Lines", countLines(program)); @@ -648,6 +646,22 @@ namespace ts { reportCountStatistic("Identity cache size", caches.identity); reportCountStatistic("Subtype cache size", caches.subtype); performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); + + const overloadMeasures = performance.getOverloadMeasures(); + const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); + const overloadStatistics = overloadMeasureGroups.map(measures => ({ + symbolName: measures[0].symbolName, + candidateCount: measures[0].candidateCount, + count: measures.length, + timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), + })); + + const sortedOverloadStatistics = overloadStatistics.sort((a, b) => b.timeMs - a.timeMs); + for (let i = 0; i < 10 && i < sortedOverloadStatistics.length; i++) { + const stat = sortedOverloadStatistics[i]; + // sys.write(`${stat.symbolName}: ${stat.timeMs}ms (count = ${stat.count}) ${sys.newLine}`); + sys.write(JSON.stringify(stat) + sys.newLine); + } } else { // Individual component times. @@ -683,12 +697,6 @@ namespace ts { for (const { name, value } of statistics) { sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine); } - - const sortedOverloadStatistics = overloadStatistics.sort((a, b) => b.timeMs - a.timeMs); - for (let i = 0; i < 10 && i < sortedOverloadStatistics.length; i++) { - const stat = sortedOverloadStatistics[i]; - sys.write(`${stat.name}: ${stat.timeMs}ms${sys.newLine}`); - } } function reportStatisticalValue(name: string, value: string) { @@ -700,10 +708,6 @@ namespace ts { } function reportTimeStatistic(name: string, timeMs: number) { - if (name.indexOf("chooseOverload") >= 0) { - overloadStatistics.push({ name, timeMs }); - return; - } reportStatisticalValue(name, (timeMs / 1000).toFixed(2) + "s"); } } From ad3c6ab0d9fe51846698b12edbbd3fdb1e6b3743 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 3 Dec 2019 20:01:48 -0800 Subject: [PATCH 03/10] Add rudimentary allocation metrics --- src/compiler/checker.ts | 12 ++++++++++ src/compiler/performance.ts | 4 ++++ src/tsc/executeCommandLine.ts | 41 +++++++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b0678656ba15d..5df404ad212e6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24157,27 +24157,39 @@ namespace ts { // signature, the subtype pass is useless. So skipping it is an optimization. if (candidates.length > 1) { + const oldCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; performance.mark("beforeChooseOverloadSubtype"); result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadSubtype"); + const newCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; performance.measureOverload("beforeChooseOverloadSubtype", "afterChooseOverloadSubtype", { kind: "subtype", nodePos: nodePosToString(node), candidateCount: candidates.length, symbolName: getSymbolName(result || candidates[0]), succeeded: !!result, + symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, + nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, + mergeCount: newCounts.nextMergeId - oldCounts.nextMergeId, + flowCount: newCounts.nextFlowId - oldCounts.nextFlowId, }); } if (!result) { + const oldCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; performance.mark("beforeChooseOverloadAssignable"); result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadAssignable"); + const newCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; performance.measureOverload("beforeChooseOverloadAssignable", "afterChooseOverloadAssignable", { kind: "assignment", nodePos: nodePosToString(node), candidateCount: candidates.length, symbolName: getSymbolName(result || candidates[0]), succeeded: !!result, + symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, + nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, + mergeCount: newCounts.nextMergeId - oldCounts.nextMergeId, + flowCount: newCounts.nextFlowId - oldCounts.nextFlowId, }); } if (result) { diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 945be1342dcb2..1d5dbd4dda9fa 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -26,6 +26,10 @@ namespace ts.performance { nodePos: string, symbolName: string, succeeded: boolean, + symbolCount: number, + nodeCount: number, + mergeCount: number, + flowCount: number, } export interface ChooseOverloadMeasure extends ChooseOverloadStats { diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index c98a8328e7d98..0384094ddceef 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -654,14 +654,43 @@ namespace ts { candidateCount: measures[0].candidateCount, count: measures.length, timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), + symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), + nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), + mergeCount: measures.map(m => m.mergeCount).reduce((a, b) => a + b, 0), + flowCount: measures.map(m => m.flowCount).reduce((a, b) => a + b, 0), })); - const sortedOverloadStatistics = overloadStatistics.sort((a, b) => b.timeMs - a.timeMs); - for (let i = 0; i < 10 && i < sortedOverloadStatistics.length; i++) { - const stat = sortedOverloadStatistics[i]; - // sys.write(`${stat.symbolName}: ${stat.timeMs}ms (count = ${stat.count}) ${sys.newLine}`); + const topCount = 5; + + sys.write("Top " + topCount + " by time" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.timeMs - a.timeMs))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); + + sys.write("Top " + topCount + " by symbols" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.symbolCount - a.symbolCount))) { sys.write(JSON.stringify(stat) + sys.newLine); } + sys.write(sys.newLine); + + sys.write("Top " + topCount + " by nodes" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.nodeCount - a.nodeCount))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); + + sys.write("Top " + topCount + " by merges" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.mergeCount - a.mergeCount))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); + + sys.write("Top " + topCount + " by flows" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.flowCount - a.flowCount))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); } else { // Individual component times. @@ -681,6 +710,10 @@ namespace ts { performance.disable(); } + function takeAtMost(count: number, array: readonly T[]): readonly T[] { + return array.slice(0, Math.min(array.length, count)); + } + function reportStatistics() { let nameSize = 0; let valueSize = 0; From 4e46e3585f4f821818848fd193e729f9fbe1ac3b Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 3 Dec 2019 20:09:21 -0800 Subject: [PATCH 04/10] Restore `kind` property --- src/tsc/executeCommandLine.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index 0384094ddceef..428810c78ee9b 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -651,6 +651,7 @@ namespace ts { const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); const overloadStatistics = overloadMeasureGroups.map(measures => ({ symbolName: measures[0].symbolName, + kind: measures[0].kind, candidateCount: measures[0].candidateCount, count: measures.length, timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), From c92632243027ee4e3075f0b2e0cb4352f8f8aad2 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 10:46:53 -0800 Subject: [PATCH 05/10] Fix lint error --- src/compiler/performance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 1d5dbd4dda9fa..4022e27bc84ce 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -138,7 +138,7 @@ namespace ts.performance { }); } - export function getOverloadMeasures(): ReadonlyArray { + export function getOverloadMeasures(): readonly ChooseOverloadMeasure[] { return overloadMeasures; } From e38b9dc22efc81d3d382a2f069ef92e6dd1822b7 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 15:59:03 -0800 Subject: [PATCH 06/10] Report type relation cache sizes --- src/compiler/checker.ts | 28 ++++++++++++++++++-------- src/compiler/performance.ts | 7 +++++-- src/tsc/executeCommandLine.ts | 38 +++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5df404ad212e6..3ef22ddbe59b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24157,11 +24157,11 @@ namespace ts { // signature, the subtype pass is useless. So skipping it is an optimization. if (candidates.length > 1) { - const oldCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; + const oldCounts = { nextSymbolId, nextNodeId, subtype: subtypeRelation.size, assignable: assignableRelation.size, comparable: comparableRelation.size, identity: identityRelation.size, enum: enumRelation.size }; performance.mark("beforeChooseOverloadSubtype"); result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadSubtype"); - const newCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; + const newCounts = { nextSymbolId, nextNodeId, subtype: subtypeRelation.size, assignable: assignableRelation.size, comparable: comparableRelation.size, identity: identityRelation.size, enum: enumRelation.size }; performance.measureOverload("beforeChooseOverloadSubtype", "afterChooseOverloadSubtype", { kind: "subtype", nodePos: nodePosToString(node), @@ -24170,16 +24170,25 @@ namespace ts { succeeded: !!result, symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, - mergeCount: newCounts.nextMergeId - oldCounts.nextMergeId, - flowCount: newCounts.nextFlowId - oldCounts.nextFlowId, + subtypeCount: newCounts.subtype - oldCounts.subtype, + assignableCount: newCounts.assignable - oldCounts.assignable, + comparableCount: newCounts.comparable - oldCounts.comparable, + identityCount: newCounts.identity - oldCounts.identity, + enumCount: newCounts.enum - oldCounts.enum, }); } if (!result) { - const oldCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; + // const subtypeRelation = createMap(); + // const assignableRelation = createMap(); + // const comparableRelation = createMap(); + // const identityRelation = createMap(); + // const enumRelation = createMap(); + + const oldCounts = { nextSymbolId, nextNodeId, subtype: subtypeRelation.size, assignable: assignableRelation.size, comparable: comparableRelation.size, identity: identityRelation.size, enum: enumRelation.size }; performance.mark("beforeChooseOverloadAssignable"); result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma); performance.mark("afterChooseOverloadAssignable"); - const newCounts = { nextSymbolId, nextNodeId, nextMergeId, nextFlowId }; + const newCounts = { nextSymbolId, nextNodeId, subtype: subtypeRelation.size, assignable: assignableRelation.size, comparable: comparableRelation.size, identity: identityRelation.size, enum: enumRelation.size }; performance.measureOverload("beforeChooseOverloadAssignable", "afterChooseOverloadAssignable", { kind: "assignment", nodePos: nodePosToString(node), @@ -24188,8 +24197,11 @@ namespace ts { succeeded: !!result, symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, - mergeCount: newCounts.nextMergeId - oldCounts.nextMergeId, - flowCount: newCounts.nextFlowId - oldCounts.nextFlowId, + subtypeCount: newCounts.subtype - oldCounts.subtype, + assignableCount: newCounts.assignable - oldCounts.assignable, + comparableCount: newCounts.comparable - oldCounts.comparable, + identityCount: newCounts.identity - oldCounts.identity, + enumCount: newCounts.enum - oldCounts.enum, }); } if (result) { diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 4022e27bc84ce..eec39b9a045f1 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -28,8 +28,11 @@ namespace ts.performance { succeeded: boolean, symbolCount: number, nodeCount: number, - mergeCount: number, - flowCount: number, + subtypeCount: number, + assignableCount: number, + comparableCount: number, + identityCount: number, + enumCount: number, } export interface ChooseOverloadMeasure extends ChooseOverloadStats { diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index 428810c78ee9b..87e4b4ba7a266 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -650,6 +650,7 @@ namespace ts { const overloadMeasures = performance.getOverloadMeasures(); const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); const overloadStatistics = overloadMeasureGroups.map(measures => ({ + nodePos: measures[0].nodePos, symbolName: measures[0].symbolName, kind: measures[0].kind, candidateCount: measures[0].candidateCount, @@ -657,8 +658,11 @@ namespace ts { timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), - mergeCount: measures.map(m => m.mergeCount).reduce((a, b) => a + b, 0), - flowCount: measures.map(m => m.flowCount).reduce((a, b) => a + b, 0), + subtypeCount: measures.map(m => m.subtypeCount).reduce((a, b) => a + b, 0), + assignableCount: measures.map(m => m.assignableCount).reduce((a, b) => a + b, 0), + comparableCount: measures.map(m => m.comparableCount).reduce((a, b) => a + b, 0), + identityCount: measures.map(m => m.identityCount).reduce((a, b) => a + b, 0), + enumCount: measures.map(m => m.enumCount).reduce((a, b) => a + b, 0), })); const topCount = 5; @@ -675,23 +679,41 @@ namespace ts { } sys.write(sys.newLine); - sys.write("Top " + topCount + " by nodes" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.nodeCount - a.nodeCount))) { + // sys.write("Top " + topCount + " by nodes" + sys.newLine); + // for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.nodeCount - a.nodeCount))) { + // sys.write(JSON.stringify(stat) + sys.newLine); + // } + // sys.write(sys.newLine); + + sys.write("Top " + topCount + " by subtype checks" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.subtypeCount - a.subtypeCount))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); + + sys.write("Top " + topCount + " by assignability checks" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.assignableCount - a.assignableCount))) { sys.write(JSON.stringify(stat) + sys.newLine); } sys.write(sys.newLine); - sys.write("Top " + topCount + " by merges" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.mergeCount - a.mergeCount))) { + sys.write("Top " + topCount + " by comparability checks" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.comparableCount - a.comparableCount))) { sys.write(JSON.stringify(stat) + sys.newLine); } sys.write(sys.newLine); - sys.write("Top " + topCount + " by flows" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.flowCount - a.flowCount))) { + sys.write("Top " + topCount + " by identity checks" + sys.newLine); + for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.identityCount - a.identityCount))) { sys.write(JSON.stringify(stat) + sys.newLine); } sys.write(sys.newLine); + + // sys.write("Top " + topCount + " by enum checks" + sys.newLine); + // for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.enumCount - a.enumCount))) { + // sys.write(JSON.stringify(stat) + sys.newLine); + // } + // sys.write(sys.newLine); } else { // Individual component times. From 5b6e751c033f2ac70973c7f631db01dfb92af1df Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 15:59:39 -0800 Subject: [PATCH 07/10] Report per call-site --- src/tsc/executeCommandLine.ts | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index 87e4b4ba7a266..1bae5e7029f30 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -648,22 +648,23 @@ namespace ts { performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); const overloadMeasures = performance.getOverloadMeasures(); - const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); - const overloadStatistics = overloadMeasureGroups.map(measures => ({ - nodePos: measures[0].nodePos, - symbolName: measures[0].symbolName, - kind: measures[0].kind, - candidateCount: measures[0].candidateCount, - count: measures.length, - timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), - symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), - nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), - subtypeCount: measures.map(m => m.subtypeCount).reduce((a, b) => a + b, 0), - assignableCount: measures.map(m => m.assignableCount).reduce((a, b) => a + b, 0), - comparableCount: measures.map(m => m.comparableCount).reduce((a, b) => a + b, 0), - identityCount: measures.map(m => m.identityCount).reduce((a, b) => a + b, 0), - enumCount: measures.map(m => m.enumCount).reduce((a, b) => a + b, 0), - })); + const overloadStatistics = overloadMeasures.slice(); + // const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); + // const overloadStatistics = overloadMeasureGroups.map(measures => ({ + // nodePos: measures[0].nodePos, + // symbolName: measures[0].symbolName, + // kind: measures[0].kind, + // candidateCount: measures[0].candidateCount, + // count: measures.length, + // timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), + // symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), + // nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), + // subtypeCount: measures.map(m => m.subtypeCount).reduce((a, b) => a + b, 0), + // assignableCount: measures.map(m => m.assignableCount).reduce((a, b) => a + b, 0), + // comparableCount: measures.map(m => m.comparableCount).reduce((a, b) => a + b, 0), + // identityCount: measures.map(m => m.identityCount).reduce((a, b) => a + b, 0), + // enumCount: measures.map(m => m.enumCount).reduce((a, b) => a + b, 0), + // })); const topCount = 5; From 90a41891060bf2e131ab991a1678257c6a7bd3ff Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 16:39:09 -0800 Subject: [PATCH 08/10] Add more disambiguation --- src/compiler/checker.ts | 14 +++++++++++--- src/compiler/performance.ts | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ef22ddbe59b8..b117bbbd80c71 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24167,6 +24167,7 @@ namespace ts { nodePos: nodePosToString(node), candidateCount: candidates.length, symbolName: getSymbolName(result || candidates[0]), + jsxName: getJsxElementName(node), succeeded: !!result, symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, @@ -24194,6 +24195,7 @@ namespace ts { nodePos: nodePosToString(node), candidateCount: candidates.length, symbolName: getSymbolName(result || candidates[0]), + jsxName: getJsxElementName(node), succeeded: !!result, symbolCount: newCounts.nextSymbolId - oldCounts.nextSymbolId, nodeCount: newCounts.nextNodeId - oldCounts.nextNodeId, @@ -24297,14 +24299,20 @@ namespace ts { function getSymbolName(signature: Signature): string { const decl = signature.declaration; const symbol = decl && getSymbolOfNode(decl); - return symbol ? getSymbolNameRecursive(symbol) : "_Unknown_"; + const name = symbol ? getSymbolNameRecursive(symbol) : "_Unknown_"; + return name; + } + + function getJsxElementName(node: Node): string | undefined { + return isJsxOpeningLikeElement(node) ? getTextOfNode(node.tagName) : undefined; } function getSymbolNameRecursive(symbol: Symbol): string { const name = unescapeLeadingUnderscores(symbol.escapedName); + const distinctName = name.indexOf("__") >= 0 ? `${name}::${getSymbolId(symbol)}` : name; return symbol.parent - ? (getSymbolNameRecursive(symbol.parent) || "_Unknown_") + "." + name - : name; + ? (getSymbolNameRecursive(symbol.parent) || "_Unknown_") + "." + distinctName + : distinctName; } function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index eec39b9a045f1..a12eef9fe7cad 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -25,6 +25,7 @@ namespace ts.performance { candidateCount: number, nodePos: string, symbolName: string, + jsxName: string | undefined, succeeded: boolean, symbolCount: number, nodeCount: number, From 7fe16cd265f80b62f00d0d86538a2f72f27dd48a Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 19:20:58 -0800 Subject: [PATCH 09/10] Improve de-duping --- src/compiler/checker.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b117bbbd80c71..938bf28245789 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24299,8 +24299,7 @@ namespace ts { function getSymbolName(signature: Signature): string { const decl = signature.declaration; const symbol = decl && getSymbolOfNode(decl); - const name = symbol ? getSymbolNameRecursive(symbol) : "_Unknown_"; - return name; + return symbol ? getSymbolNameRecursive(symbol) : "_Unknown_"; } function getJsxElementName(node: Node): string | undefined { @@ -24308,11 +24307,24 @@ namespace ts { } function getSymbolNameRecursive(symbol: Symbol): string { - const name = unescapeLeadingUnderscores(symbol.escapedName); - const distinctName = name.indexOf("__") >= 0 ? `${name}::${getSymbolId(symbol)}` : name; - return symbol.parent - ? (getSymbolNameRecursive(symbol.parent) || "_Unknown_") + "." + distinctName - : distinctName; + let name = unescapeLeadingUnderscores(symbol.escapedName); + if (name === "__type") { + if (symbol.declarations.length === 1) { + const decl = symbol.declarations[0]; + if (decl.kind === SyntaxKind.FunctionType) { + const parent = decl.parent; + if (parent && isTypeAliasDeclaration(parent)) { + name = getTextOfNode(parent.name); + } + } + } + } + if (name.startsWith("__") && !symbol.parent) { + name = name + "::" + getSymbolId(symbol); + } + return symbol.parent + ? (getSymbolNameRecursive(symbol.parent) || "_Unknown_") + "." + name + : name; } function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { From b1456ae8de59ca0f085834e69bf86c93034d5bbe Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 4 Dec 2019 19:21:13 -0800 Subject: [PATCH 10/10] Print both individual and aggregated statistics --- src/tsc/executeCommandLine.ts | 94 ++++++++++++++--------------------- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/src/tsc/executeCommandLine.ts b/src/tsc/executeCommandLine.ts index 1bae5e7029f30..71a58758263e3 100644 --- a/src/tsc/executeCommandLine.ts +++ b/src/tsc/executeCommandLine.ts @@ -649,72 +649,42 @@ namespace ts { const overloadMeasures = performance.getOverloadMeasures(); const overloadStatistics = overloadMeasures.slice(); - // const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName); - // const overloadStatistics = overloadMeasureGroups.map(measures => ({ - // nodePos: measures[0].nodePos, - // symbolName: measures[0].symbolName, - // kind: measures[0].kind, - // candidateCount: measures[0].candidateCount, - // count: measures.length, - // timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), - // symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), - // nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), - // subtypeCount: measures.map(m => m.subtypeCount).reduce((a, b) => a + b, 0), - // assignableCount: measures.map(m => m.assignableCount).reduce((a, b) => a + b, 0), - // comparableCount: measures.map(m => m.comparableCount).reduce((a, b) => a + b, 0), - // identityCount: measures.map(m => m.identityCount).reduce((a, b) => a + b, 0), - // enumCount: measures.map(m => m.enumCount).reduce((a, b) => a + b, 0), - // })); + const overloadMeasureGroups = group(overloadMeasures, s => s.kind + "::" + s.symbolName + (s.jsxName ? "::" + s.jsxName : "")); + const groupedOverloadStatistics = overloadMeasureGroups.map(measures => ({ + symbolName: measures[0].symbolName, + jsxName: measures[0].jsxName, + kind: measures[0].kind, + candidateCount: measures[0].candidateCount, + count: measures.length, + timeMs: measures.map(m => m.timeMs).reduce((a, b) => a + b, 0), + symbolCount: measures.map(m => m.symbolCount).reduce((a, b) => a + b, 0), + nodeCount: measures.map(m => m.nodeCount).reduce((a, b) => a + b, 0), + subtypeCount: measures.map(m => m.subtypeCount).reduce((a, b) => a + b, 0), + assignableCount: measures.map(m => m.assignableCount).reduce((a, b) => a + b, 0), + comparableCount: measures.map(m => m.comparableCount).reduce((a, b) => a + b, 0), + identityCount: measures.map(m => m.identityCount).reduce((a, b) => a + b, 0), + enumCount: measures.map(m => m.enumCount).reduce((a, b) => a + b, 0), + })); const topCount = 5; - sys.write("Top " + topCount + " by time" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.timeMs - a.timeMs))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } + sys.write("Individual callsites" + sys.newLine); sys.write(sys.newLine); - sys.write("Top " + topCount + " by symbols" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.symbolCount - a.symbolCount))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } - sys.write(sys.newLine); + writeTop(overloadStatistics, topCount, "time", s => s.timeMs); + writeTop(overloadStatistics, topCount, "symbols", s => s.symbolCount); + writeTop(overloadStatistics, topCount, "subtype checks", s => s.subtypeCount); + writeTop(overloadStatistics, topCount, "assignability checks", s => s.assignableCount); + writeTop(overloadStatistics, topCount, "identity checks", s => s.identityCount); - // sys.write("Top " + topCount + " by nodes" + sys.newLine); - // for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.nodeCount - a.nodeCount))) { - // sys.write(JSON.stringify(stat) + sys.newLine); - // } - // sys.write(sys.newLine); - - sys.write("Top " + topCount + " by subtype checks" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.subtypeCount - a.subtypeCount))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } + sys.write("Aggregated by function" + sys.newLine); sys.write(sys.newLine); - sys.write("Top " + topCount + " by assignability checks" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.assignableCount - a.assignableCount))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } - sys.write(sys.newLine); - - sys.write("Top " + topCount + " by comparability checks" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.comparableCount - a.comparableCount))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } - sys.write(sys.newLine); - - sys.write("Top " + topCount + " by identity checks" + sys.newLine); - for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.identityCount - a.identityCount))) { - sys.write(JSON.stringify(stat) + sys.newLine); - } - sys.write(sys.newLine); - - // sys.write("Top " + topCount + " by enum checks" + sys.newLine); - // for (const stat of takeAtMost(topCount, overloadStatistics.sort((a, b) => b.enumCount - a.enumCount))) { - // sys.write(JSON.stringify(stat) + sys.newLine); - // } - // sys.write(sys.newLine); + writeTop(groupedOverloadStatistics, topCount, "time", s => s.timeMs); + writeTop(groupedOverloadStatistics, topCount, "symbols", s => s.symbolCount); + writeTop(groupedOverloadStatistics, topCount, "subtype checks", s => s.subtypeCount); + writeTop(groupedOverloadStatistics, topCount, "assignability checks", s => s.assignableCount); + writeTop(groupedOverloadStatistics, topCount, "identity checks", s => s.identityCount); } else { // Individual component times. @@ -734,6 +704,14 @@ namespace ts { performance.disable(); } + function writeTop(collection: T[], count: number, propName: string, propFn: (t: T) => number) { + sys.write("Top " + count + " by " + propName + sys.newLine); + for (const stat of takeAtMost(count, collection.sort((a, b) => propFn(b) - propFn(a)))) { + sys.write(JSON.stringify(stat) + sys.newLine); + } + sys.write(sys.newLine); + } + function takeAtMost(count: number, array: readonly T[]): readonly T[] { return array.slice(0, Math.min(array.length, count)); }