From 46e3513ff95df1942b0ff50225bfdd4fb7ae66a1 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 13 May 2022 14:24:59 -0700 Subject: [PATCH 01/10] Refactor --- src/compiler/program.ts | 4 +++- src/compiler/transformers/declarations.ts | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 48743e9303463..30b80770dc661 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2483,7 +2483,9 @@ namespace ts { return runWithCancellationToken(() => { const resolver = getTypeChecker().getEmitResolver(sourceFile, cancellationToken); // Don't actually write any files since we're just getting diagnostics. - return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray; + const emitHost = getEmitHost(noop); + const result = transformNodes(resolver, emitHost, factory, options, sourceFile ? [sourceFile] : filter(emitHost.getSourceFiles(), isSourceFileNotJson), [transformDeclarations], /*allowDtsFiles*/ false); + return result.diagnostics || emptyArray; }); } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index b855fd4b8dab5..557c76f9c4e84 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -1,11 +1,5 @@ /*@internal*/ namespace ts { - export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] | undefined { - const compilerOptions = host.getCompilerOptions(); - const result = transformNodes(resolver, host, factory, compilerOptions, file ? [file] : filter(host.getSourceFiles(), isSourceFileNotJson), [transformDeclarations], /*allowDtsFiles*/ false); - return result.diagnostics; - } - function hasInternalAnnotation(range: CommentRange, currentSourceFile: SourceFile) { const comment = currentSourceFile.text.substring(range.pos, range.end); return stringContains(comment, "@internal"); From 5bba57e914c43a54b8a304bfc4cac963a2fe9ace Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 17 May 2022 16:32:16 -0700 Subject: [PATCH 02/10] Cache dts emit results Fixes #43995 --- src/compiler/builder.ts | 2 +- src/compiler/builderState.ts | 29 ++--- src/compiler/builderStatePublic.ts | 1 - src/compiler/emitter.ts | 106 ++++++++++++++---- src/compiler/program.ts | 26 ++++- src/compiler/types.ts | 18 ++- .../unittests/services/languageService.ts | 2 - .../tsserver/projectReferenceCompileOnSave.ts | 90 +++++++-------- 8 files changed, 188 insertions(+), 86 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 0485f10cbf071..5e978936b4076 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1173,7 +1173,7 @@ namespace ts { const info = state.fileInfos.get(file.resolvedPath)!; const signature = state.currentAffectedFilesSignatures?.get(file.resolvedPath) || info.signature; if (signature === file.version) { - const newSignature = (computeHash || generateDjb2Hash)(data?.sourceMapUrlPos !== undefined ? text.substring(0, data.sourceMapUrlPos) : text); + const newSignature = BuilderState.getSignatureFromWriteFileText(text, data, computeHash); if (newSignature !== file.version) { // Update it if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ||= new Set()).add(file.resolvedPath); if (state.exportedModulesMap) BuilderState.updateExportedModules(file, file.exportedModulesFromDeclarationEmit, state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap()); diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index f501eda66a1d1..6579ef37c6743 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -3,8 +3,8 @@ namespace ts { export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput { const outputFiles: OutputFile[] = []; - const { emitSkipped, diagnostics, exportedModulesFromDeclarationEmit } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit); - return { outputFiles, emitSkipped, diagnostics, exportedModulesFromDeclarationEmit }; + const { emitSkipped, diagnostics } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit); + return { outputFiles, emitSkipped, diagnostics }; function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { outputFiles.push({ name: fileName, writeByteOrderMark, text }); @@ -394,6 +394,10 @@ namespace ts { state.hasCalledUpdateShapeSignature.add(path); } + export function getSignatureFromWriteFileText(text: string, data: WriteFileCallbackData | undefined, computeHash: ComputeHash | undefined) { + return (computeHash || generateDjb2Hash)(data?.sourceMapUrlPos !== undefined ? text.substring(0, data.sourceMapUrlPos) : text); + } + /** * Returns if the shape of the signature has changed since last emit */ @@ -412,22 +416,21 @@ namespace ts { const prevSignature = info.signature; let latestSignature: string | undefined; if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) { - const emitOutput = getFileEmitOutput( - programOfThisState, + programOfThisState.emit( sourceFile, - /*emitOnlyDtsFiles*/ true, + (fileName, text, _writeByteOrderMark, _onError, _sourceFiles, data) => { + Debug.assert(isDeclarationFileName(fileName), "File extension for signature expected to be dts"); + Debug.assert(latestSignature === undefined, "Only one file should be set"); + latestSignature = getSignatureFromWriteFileText(text, data, computeHash); + if (exportedModulesMapCache && latestSignature !== prevSignature) { + updateExportedModules(sourceFile, data?.exportedModulesFromDeclarationEmit, exportedModulesMapCache); + } + }, cancellationToken, + /*emitOnlyDtsFiles*/ true, /*customTransformers*/ undefined, /*forceDtsEmit*/ true ); - const firstDts = firstOrUndefined(emitOutput.outputFiles); - if (firstDts) { - Debug.assert(isDeclarationFileName(firstDts.name), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`); - latestSignature = (computeHash || generateDjb2Hash)(firstDts.text); - if (exportedModulesMapCache && latestSignature !== prevSignature) { - updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache); - } - } } // Default is to use file version as signature if (latestSignature === undefined) { diff --git a/src/compiler/builderStatePublic.ts b/src/compiler/builderStatePublic.ts index ce542b0825b80..1be4f84b5db4e 100644 --- a/src/compiler/builderStatePublic.ts +++ b/src/compiler/builderStatePublic.ts @@ -3,7 +3,6 @@ namespace ts { outputFiles: OutputFile[]; emitSkipped: boolean; /* @internal */ diagnostics: readonly Diagnostic[]; - /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; } export interface OutputFile { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c83713750f2a6..7d56469ec354b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -276,6 +276,15 @@ namespace ts { return Debug.fail(`project ${configFile.options.configFilePath} expected to have at least one output`); } + /*@internal*/ + export function getCacheDtsEmitResultKey(sourceFileOrBundle: SourceFile | Bundle, options: CompilerOptions) { + return isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.resolvedPath : outFile(options) as Path; + } + + function isForceDtsEmitResult(result: CacheDtsEmitResult | undefined): result is ForceDtsEmitResult { + return !!result && !(result as TransformationResult).transformed; + } + /*@internal*/ // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile | undefined, { scriptTransformers, declarationTransformers }: EmitTransformers, emitOnlyDtsFiles?: boolean, onlyBuildInfo?: boolean, forceDtsEmit?: boolean): EmitResult { @@ -288,7 +297,6 @@ namespace ts { const { enter, exit } = performance.createTimer("printTime", "beforePrint", "afterPrint"); let bundleBuildInfo: BundleBuildInfo | undefined; let emitSkipped = false; - let exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined; // Emit each output file enter(); @@ -308,7 +316,6 @@ namespace ts { diagnostics: emitterDiagnostics.getDiagnostics(), emittedFiles: emittedFilesList, sourceMaps: sourceMapDataList, - exportedModulesFromDeclarationEmit }; function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle | undefined) { @@ -411,8 +418,7 @@ namespace ts { substituteNode: transform.substituteNode, }); - Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform"); - printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform.transformed[0], printer, compilerOptions); + printSourceFileOrBundle(jsFilePath, sourceMapFilePath, transform, printer, compilerOptions); // Clean up emit nodes on parse tree transform.dispose(); @@ -433,12 +439,48 @@ namespace ts { const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson); // Setup and perform the transformation to retrieve declarations from the input files const inputListOrBundle = outFile(compilerOptions) ? [factory.createBundle(filesForEmit, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : filesForEmit; - if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) { + // Use existing result: + const key = getCacheDtsEmitResultKey(sourceFileOrBundle, compilerOptions); + const cache = host.getDtsEmitResultCache(); + const existing = declarationTransformers.length === 1 ? cache.get(key) : undefined; + if (isForceDtsEmitResult(existing)) { + if (existing.diagnostics?.length) { + for (const diagnostic of existing.diagnostics) { + emitterDiagnostics.add(diagnostic); + } + } + const declBlocked = !!existing.diagnostics?.length || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit; + emitSkipped = emitSkipped || declBlocked; + if (!declBlocked || forceDtsEmit) { + // Write the output file + if (declarationMapPath && !forceDtsEmit) { + if (sourceMapDataList && existing.sourceMapData) { + sourceMapDataList.push(existing.sourceMapData); + } + writeFile(host, emitterDiagnostics, declarationMapPath, existing.sourceMap!, /*writeByteOrderMark*/ false, sourceFiles); + } + writeFile( + host, + emitterDiagnostics, + declarationFilePath, + existing.text, + !!compilerOptions.emitBOM, + filesForEmit, + { + sourceMapUrlPos: existing.sourceMapUrlPos, + exportedModulesFromDeclarationEmit: existing.exportedModulesFromDeclarationEmit, + } + ); + } + return; + } + + if (!existing && emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) { // Checker wont collect the linked aliases since thats only done when declaration is enabled. // Do that here when emitting only dts files filesForEmit.forEach(collectLinkedAliases); } - const declarationTransform = transformNodes(resolver, host, factory, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false); + const declarationTransform = existing || transformNodes(resolver, host, factory, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false); if (length(declarationTransform.diagnostics)) { for (const diagnostic of declarationTransform.diagnostics!) { emitterDiagnostics.add(diagnostic); @@ -451,8 +493,8 @@ namespace ts { noEmitHelpers: true, module: compilerOptions.module, target: compilerOptions.target, - sourceMap: compilerOptions.sourceMap, - inlineSourceMap: compilerOptions.inlineSourceMap, + // Emit maps when there is noEmit not set with forceDtsEmit + sourceMap: (!forceDtsEmit || !compilerOptions.noEmit) && compilerOptions.declarationMap, extendedDiagnostics: compilerOptions.extendedDiagnostics, onlyPrintJsDocStyle: true, writeBundleFileInfo: !!bundleBuildInfo, @@ -472,24 +514,19 @@ namespace ts { const declBlocked = (!!declarationTransform.diagnostics && !!declarationTransform.diagnostics.length) || !!host.isEmitBlocked(declarationFilePath) || !!compilerOptions.noEmit; emitSkipped = emitSkipped || declBlocked; if (!declBlocked || forceDtsEmit) { - Debug.assert(declarationTransform.transformed.length === 1, "Should only see one output from the decl transform"); printSourceFileOrBundle( declarationFilePath, declarationMapPath, - declarationTransform.transformed[0], + declarationTransform, declarationPrinter, { - sourceMap: !forceDtsEmit && compilerOptions.declarationMap, + sourceMap: printerOptions.sourceMap, sourceRoot: compilerOptions.sourceRoot, mapRoot: compilerOptions.mapRoot, extendedDiagnostics: compilerOptions.extendedDiagnostics, // Explicitly do not passthru either `inline` option } ); - if (forceDtsEmit && declarationTransform.transformed[0].kind === SyntaxKind.SourceFile) { - const sourceFile = declarationTransform.transformed[0]; - exportedModulesFromDeclarationEmit = sourceFile.exportedModulesFromDeclarationEmit; - } } declarationTransform.dispose(); if (bundleBuildInfo) bundleBuildInfo.dts = declarationPrinter.bundleFileInfo; @@ -509,7 +546,10 @@ namespace ts { forEachChild(node, collectLinkedAliases); } - function printSourceFileOrBundle(jsFilePath: string, sourceMapFilePath: string | undefined, sourceFileOrBundle: SourceFile | Bundle, printer: Printer, mapOptions: SourceMapOptions) { + function printSourceFileOrBundle( + jsFilePath: string, sourceMapFilePath: string | undefined, transform: TransformationResult, printer: Printer, mapOptions: SourceMapOptions) { + Debug.assert(transform.transformed.length === 1, "Should only see one output from the transform"); + const sourceFileOrBundle = transform.transformed[0]; const bundle = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle : undefined; const sourceFile = sourceFileOrBundle.kind === SyntaxKind.SourceFile ? sourceFileOrBundle : undefined; const sourceFiles = bundle ? bundle.sourceFiles : [sourceFile!]; @@ -532,9 +572,11 @@ namespace ts { } let sourceMapUrlPos; + let sourceMap; + let sourceMapData; if (sourceMapGenerator) { if (sourceMapDataList) { - sourceMapDataList.push({ + sourceMapDataList.push(sourceMapData = { inputSourceFileNames: sourceMapGenerator.getSources(), sourceMap: sourceMapGenerator.toJSON() }); @@ -555,8 +597,8 @@ namespace ts { // Write the source map if (sourceMapFilePath) { - const sourceMap = sourceMapGenerator.toString(); - writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles); + sourceMap = sourceMapGenerator.toString(); + if (!forceDtsEmit) writeFile(host, emitterDiagnostics, sourceMapFilePath, sourceMap, /*writeByteOrderMark*/ false, sourceFiles); } } else { @@ -564,7 +606,30 @@ namespace ts { } // Write the output file - writeFile(host, emitterDiagnostics, jsFilePath, writer.getText(), !!compilerOptions.emitBOM, sourceFiles, { sourceMapUrlPos }); + writeFile( + host, + emitterDiagnostics, + jsFilePath, + writer.getText(), + !!compilerOptions.emitBOM, + sourceFiles, + { + sourceMapUrlPos, + exportedModulesFromDeclarationEmit: sourceFile?.exportedModulesFromDeclarationEmit, + } + ); + if (forceDtsEmit) { + // Store the result + const key = getCacheDtsEmitResultKey(sourceFileOrBundle, compilerOptions); + host.getDtsEmitResultCache().set(key, { + diagnostics: transform.diagnostics, + text: writer.getText(), + sourceMap, + sourceMapData, + sourceMapUrlPos, + exportedModulesFromDeclarationEmit: sourceFile?.exportedModulesFromDeclarationEmit, + }); + } // Reset state writer.clear(); @@ -833,6 +898,7 @@ namespace ts { getSourceFileFromReference: returnUndefined, redirectTargetsMap: createMultiMap(), getFileIncludeReasons: notImplemented, + getDtsEmitResultCache: () => new Map(), }; emitFiles( notImplementedResolver, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 30b80770dc661..510ce2009a19d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1120,6 +1120,7 @@ namespace ts { // Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it. let redirectTargetsMap = createMultiMap(); let usesUriStyleNodeCoreModules = false; + let cacheDtsEmitResult: ESMap | undefined; /** * map with @@ -1943,6 +1944,7 @@ namespace ts { getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref), redirectTargetsMap, getFileIncludeReasons: program.getFileIncludeReasons, + getDtsEmitResultCache: () => cacheDtsEmitResult ||= new Map(), }; } @@ -2484,8 +2486,28 @@ namespace ts { const resolver = getTypeChecker().getEmitResolver(sourceFile, cancellationToken); // Don't actually write any files since we're just getting diagnostics. const emitHost = getEmitHost(noop); - const result = transformNodes(resolver, emitHost, factory, options, sourceFile ? [sourceFile] : filter(emitHost.getSourceFiles(), isSourceFileNotJson), [transformDeclarations], /*allowDtsFiles*/ false); - return result.diagnostics || emptyArray; + if (sourceFile) { + if (!outFile(options)) { + // Check from cache + const existing = cacheDtsEmitResult?.get(sourceFile.resolvedPath); + if (existing) return existing.diagnostics || emptyArray; + } + const result = transformNodes(resolver, emitHost, factory, options, [sourceFile], [transformDeclarations], /*allowDtsFiles*/ false); + if (!outFile(options)) { + (cacheDtsEmitResult ||= new Map()).set(sourceFile.resolvedPath, result); + } + return result.diagnostics || emptyArray; + } + let diagnostics: DiagnosticWithLocation[] | undefined; + forEachEmittedFile(emitHost, (_emitFileNames, sourceFileOrBundle) => { + const key = getCacheDtsEmitResultKey(sourceFileOrBundle!, options); + const existing = cacheDtsEmitResult?.get(key); + if (existing) return existing.diagnostics || emptyArray; + const result = transformNodes(resolver, emitHost, factory, options, [sourceFileOrBundle!], [transformDeclarations], /*allowDtsFiles*/ false); + diagnostics = addRange(diagnostics, result.diagnostics); + (cacheDtsEmitResult ||= new Map()).set(key, result); + }); + return diagnostics || emptyArray; }); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index db071689cd303..081cf51649b37 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3864,7 +3864,8 @@ namespace ts { export type ResolvedConfigFileName = string & { _isResolvedConfigFileName: never }; export interface WriteFileCallbackData { - /*@internal*/ sourceMapUrlPos?: number; + /*@internal*/ sourceMapUrlPos: number | undefined; + /*@internal*/ exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined; } export type WriteFileCallback = ( fileName: string, @@ -3973,6 +3974,19 @@ namespace ts { /*@internal*/ export type FilePreprocessingDiagnostics = FilePreprocessingReferencedDiagnostic | FilePreprocessingFileExplainingDiagnostic; + /*@internal*/ + export interface ForceDtsEmitResult { + diagnostics: readonly DiagnosticWithLocation[] | undefined; + text: string; + sourceMap: string | undefined; + sourceMapData: SourceMapEmitResult | undefined; + sourceMapUrlPos: number | undefined; + exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined; + } + + /*@internal*/ + export type CacheDtsEmitResult = ForceDtsEmitResult | TransformationResult; + export interface Program extends ScriptReferenceHost { getCurrentDirectory(): string; /** @@ -4174,7 +4188,6 @@ namespace ts { diagnostics: readonly Diagnostic[]; emittedFiles?: string[]; // Array of files the compiler wrote to disk /* @internal */ sourceMaps?: SourceMapEmitResult[]; // Array of sourceMapData if compiler emitted sourcemaps - /* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; } /* @internal */ @@ -7104,6 +7117,7 @@ namespace ts { getProgramBuildInfo(): ProgramBuildInfo | undefined; getSourceFileFromReference: Program["getSourceFileFromReference"]; readonly redirectTargetsMap: RedirectTargetsMap; + getDtsEmitResultCache(): ESMap; } /* @internal */ diff --git a/src/testRunner/unittests/services/languageService.ts b/src/testRunner/unittests/services/languageService.ts index c3f5f019541af..b93215f4df2cc 100644 --- a/src/testRunner/unittests/services/languageService.ts +++ b/src/testRunner/unittests/services/languageService.ts @@ -62,7 +62,6 @@ export function Component(x: Config): any;` emitSkipped: true, diagnostics: emptyArray, outputFiles: emptyArray, - exportedModulesFromDeclarationEmit: undefined } ); @@ -80,7 +79,6 @@ export function Component(x: Config): any;` text: "export {};\r\n", writeByteOrderMark: false }], - exportedModulesFromDeclarationEmit: undefined } ); }); diff --git a/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts b/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts index 97132be83b75d..8ea94a596c8a8 100644 --- a/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts +++ b/src/testRunner/unittests/tsserver/projectReferenceCompileOnSave.ts @@ -139,7 +139,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -175,7 +175,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -218,7 +218,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -261,7 +261,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -315,7 +315,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -369,7 +369,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -412,7 +412,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -455,7 +455,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -509,7 +509,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -563,7 +563,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -597,7 +597,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -628,7 +628,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -666,7 +666,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -704,7 +704,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -753,7 +753,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -802,7 +802,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -840,7 +840,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -878,7 +878,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -927,7 +927,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -976,7 +976,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1017,7 +1017,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -1053,7 +1053,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1107,7 +1107,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -1161,7 +1161,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1215,7 +1215,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -1269,7 +1269,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1323,7 +1323,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -1377,7 +1377,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1431,7 +1431,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path } }).response as EmitOutput; @@ -1485,7 +1485,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: usageTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1519,7 +1519,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1568,7 +1568,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1617,7 +1617,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1666,7 +1666,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1715,7 +1715,7 @@ ${appendDts}` assert.equal(host.writtenFiles.size, 0); // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: usageConfig.path } }).response as EmitOutput; @@ -1755,7 +1755,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -1791,7 +1791,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: dependencyConfig.path } }).response as EmitOutput; @@ -1846,7 +1846,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -1900,7 +1900,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: dependencyConfig.path } }).response as EmitOutput; @@ -1955,7 +1955,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -2009,7 +2009,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: dependencyConfig.path } }).response as EmitOutput; @@ -2064,7 +2064,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -2118,7 +2118,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: dependencyConfig.path } }).response as EmitOutput; @@ -2173,7 +2173,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path } }).response as EmitOutput; @@ -2227,7 +2227,7 @@ ${appendDts}` } // Verify EmitOutput - const { exportedModulesFromDeclarationEmit: _1, ...actualEmitOutput } = session.executeCommandSeq({ + const actualEmitOutput = session.executeCommandSeq({ command: protocol.CommandTypes.EmitOutput, arguments: { file: dependencyTs.path, projectFileName: dependencyConfig.path } }).response as EmitOutput; From 2726b296002c7d7ae960de775433491568779e41 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 24 May 2022 13:22:50 -0700 Subject: [PATCH 03/10] Report aggregate statistics for solution as well as some solution perf numbers --- src/compiler/performance.ts | 246 ++++++++++--------- src/compiler/sourcemap.ts | 4 +- src/compiler/tsbuildPublic.ts | 81 +++++- src/executeCommandLine/executeCommandLine.ts | 197 +++++++++++---- 4 files changed, 350 insertions(+), 178 deletions(-) diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 3f6c23f2b0735..2dbdb390b4078 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -1,146 +1,162 @@ /*@internal*/ /** Performance measurements for the compiler. */ -namespace ts.performance { - let perfHooks: PerformanceHooks | undefined; - // when set, indicates the implementation of `Performance` to use for user timing. - // when unset, indicates user timing is unavailable or disabled. - let performanceImpl: Performance | undefined; - - export interface Timer { +namespace ts { + interface Timer { enter(): void; exit(): void; } - export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) { - return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer; - } + const nullTimer: Timer = { enter: noop, exit: noop }; + export const performance = createPerformanceTracker(); + export const solutionPerformance = createPerformanceTracker(); + + function createPerformanceTracker() { + let perfHooks: PerformanceHooks | undefined; + // when set, indicates the implementation of `Performance` to use for user timing. + // when unset, indicates user timing is unavailable or disabled. + let performanceImpl: Performance | undefined; + let enabled = false; + let timeorigin = timestamp(); + const marks = new Map(); + const counts = new Map(); + const durations = new Map(); - export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer { - let enterCount = 0; return { - enter, - exit + createTimerIf, + createTimer, + mark, + measure, + getCount, + getDuration, + forEachMeasure, + isEnabled, + enable, + disable, }; - function enter() { - if (++enterCount === 1) { - mark(startMarkName); - } + function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) { + return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer; } - function exit() { - if (--enterCount === 0) { - mark(endMarkName); - measure(measureName, startMarkName, endMarkName); + function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer { + let enterCount = 0; + return { + enter, + exit + }; + + function enter() { + if (++enterCount === 1) { + mark(startMarkName); + } } - else if (enterCount < 0) { - Debug.fail("enter/exit count does not match."); + + function exit() { + if (--enterCount === 0) { + mark(endMarkName); + measure(measureName, startMarkName, endMarkName); + } + else if (enterCount < 0) { + Debug.fail("enter/exit count does not match."); + } } } - } - export const nullTimer: Timer = { enter: noop, exit: noop }; - - let enabled = false; - let timeorigin = timestamp(); - const marks = new Map(); - const counts = new Map(); - const durations = new Map(); - - /** - * Marks a performance event. - * - * @param markName The name of the mark. - */ - export function mark(markName: string) { - if (enabled) { - const count = counts.get(markName) ?? 0; - counts.set(markName, count + 1); - marks.set(markName, timestamp()); - performanceImpl?.mark(markName); + /** + * Marks a performance event. + * + * @param markName The name of the mark. + */ + function mark(markName: string) { + if (enabled) { + const count = counts.get(markName) ?? 0; + counts.set(markName, count + 1); + marks.set(markName, timestamp()); + performanceImpl?.mark(markName); + } } - } - /** - * 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 not supplied, the point at which the - * profiler was enabled is used. - * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is - * used. - */ - export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { - if (enabled) { - const end = (endMarkName !== undefined ? marks.get(endMarkName) : undefined) ?? timestamp(); - const start = (startMarkName !== undefined ? marks.get(startMarkName) : undefined) ?? timeorigin; - const previousDuration = durations.get(measureName) || 0; - durations.set(measureName, previousDuration + (end - start)); - performanceImpl?.measure(measureName, startMarkName, endMarkName); + /** + * 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 not supplied, the point at which the + * profiler was enabled is used. + * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is + * used. + */ + function measure(measureName: string, startMarkName: string, endMarkName: string) { + if (enabled) { + const end = marks.get(endMarkName) ?? timestamp(); + const start = marks.get(startMarkName) ?? timeorigin; + const previousDuration = durations.get(measureName) || 0; + durations.set(measureName, previousDuration + (end - start)); + performanceImpl?.measure(measureName, startMarkName, endMarkName); + } } - } - /** - * Gets the number of times a marker was encountered. - * - * @param markName The name of the mark. - */ - export function getCount(markName: string) { - return counts.get(markName) || 0; - } + /** + * Gets the number of times a marker was encountered. + * + * @param markName The name of the mark. + */ + function getCount(markName: string) { + return counts.get(markName) || 0; + } - /** - * 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 durations.get(measureName) || 0; - } + /** + * Gets the total duration of all measurements with the supplied name. + * + * @param measureName The name of the measure whose durations should be accumulated. + */ + function getDuration(measureName: string) { + return durations.get(measureName) || 0; + } - /** - * 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) { - durations.forEach((duration, measureName) => cb(measureName, duration)); - } + /** + * Iterate over each measure, performing some action + * + * @param cb The action to perform for each measure + */ + function forEachMeasure(cb: (measureName: string, duration: number) => void) { + durations.forEach((duration, measureName) => cb(measureName, duration)); + } - /** - * Indicates whether the performance API is enabled. - */ - export function isEnabled() { - return enabled; - } + /** + * Indicates whether the performance API is enabled. + */ + function isEnabled() { + return enabled; + } - /** Enables (and resets) performance measurements for the compiler. */ - export function enable(system: System = sys) { - if (!enabled) { - enabled = true; - perfHooks ||= tryGetNativePerformanceHooks(); - if (perfHooks) { - timeorigin = perfHooks.performance.timeOrigin; - // NodeJS's Web Performance API is currently slower than expected, but we'd still like - // to be able to leverage native trace events when node is run with either `--cpu-prof` - // or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when - // running in debug mode (since its possible to generate a cpu profile while debugging). - if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) { - performanceImpl = perfHooks.performance; + /** Enables (and resets) performance measurements for the compiler. */ + function enable(system: System = sys) { + if (!enabled) { + enabled = true; + perfHooks ||= tryGetNativePerformanceHooks(); + if (perfHooks) { + timeorigin = perfHooks.performance.timeOrigin; + // NodeJS's Web Performance API is currently slower than expected, but we'd still like + // to be able to leverage native trace events when node is run with either `--cpu-prof` + // or `--prof`, if we're running with our own `--generateCpuProfile` flag, or when + // running in debug mode (since its possible to generate a cpu profile while debugging). + if (perfHooks.shouldWriteNativeEvents || system?.cpuProfilingEnabled?.() || system?.debugMode) { + performanceImpl = perfHooks.performance; + } } } + return true; } - return true; - } - /** Disables performance measurements for the compiler. */ - export function disable() { - if (enabled) { - marks.clear(); - counts.clear(); - durations.clear(); - performanceImpl = undefined; - enabled = false; + /** Disables performance measurements for the compiler. */ + function disable() { + if (enabled) { + marks.clear(); + counts.clear(); + durations.clear(); + performanceImpl = undefined; + enabled = false; + } } } } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index e3aebc849b6a3..22bb9e92424e7 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -5,9 +5,7 @@ namespace ts { } export function createSourceMapGenerator(host: EmitHost, file: string, sourceRoot: string, sourcesDirectoryPath: string, generatorOptions: SourceMapGeneratorOptions): SourceMapGenerator { - const { enter, exit } = generatorOptions.extendedDiagnostics - ? performance.createTimer("Source Map", "beforeSourcemap", "afterSourcemap") - : performance.nullTimer; + const { enter, exit } = performance.createTimerIf(!!generatorOptions.extendedDiagnostics, "Source Map", "beforeSourcemap", "afterSourcemap"); // Current source map file and its index in the sources list const rawSources: string[] = []; diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 7bbc26feb10a9..1dc941d5aa894 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -378,7 +378,7 @@ namespace ts { if (value) { return isParsedCommandLine(value) ? value : undefined; } - + solutionPerformance.mark("beforeParseConfigFile"); let diagnostic: Diagnostic | undefined; const { parseConfigFileHost, baseCompilerOptions, baseWatchOptions, extendedConfigCache, host } = state; let parsed: ParsedCommandLine | undefined; @@ -392,6 +392,8 @@ namespace ts { parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop; } configFileCache.set(configFilePath, parsed || diagnostic!); + solutionPerformance.mark("afterParseConfigFile"); + solutionPerformance.measure("ParseConfigFile", "beforeParseConfigFile", "afterParseConfigFile"); return parsed; } @@ -451,6 +453,7 @@ namespace ts { } function createStateBuildOrder(state: SolutionBuilderState) { + solutionPerformance.mark("beforeCreateBuildOrder"); const buildOrder = createBuildOrder(state, state.rootNames.map(f => resolveProjectName(state, f))); // Clear all to ResolvedConfigFilePaths cache to start fresh @@ -507,6 +510,8 @@ namespace ts { { onDeleteValue: existingMap => existingMap.forEach(closeFileWatcher) } ); } + solutionPerformance.mark("afterCreateBuildOrder"); + solutionPerformance.measure("CreateBuildOrder", "beforeCreateBuildOrder", "afterCreateBuildOrder"); return state.buildOrder = buildOrder; } @@ -708,6 +713,7 @@ namespace ts { if (updateOutputFileStampsPending) { updateOutputTimestamps(state, config, projectPath); } + solutionPerformance.mark("timestampUpdated"); return doneInvalidatedProject(state, projectPath); } }; @@ -821,6 +827,8 @@ namespace ts { function done(cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) { executeSteps(BuildStep.Done, cancellationToken, writeFile, customTransformers); + if (kind === InvalidatedProjectKind.Build) solutionPerformance.mark("projectsBuilt"); + else solutionPerformance.mark("bundlesUpdated"); return doneInvalidatedProject(state, projectPath); } @@ -961,6 +969,7 @@ namespace ts { let anyDtsChanged = false; const emitterDiagnostics = createDiagnosticCollection(); const emittedOutputs = new Map(); + solutionPerformance.mark("beforeOutputFilesWrite"); outputFiles.forEach(({ name, text, writeByteOrderMark }) => { let priorChangeTime: Date | undefined; if (!anyDtsChanged && isDeclarationFileName(name)) { @@ -980,6 +989,8 @@ namespace ts { newestDeclarationFileContentChangedTime = newer(priorChangeTime, newestDeclarationFileContentChangedTime); } }); + solutionPerformance.mark("afterOutputFilesWrite"); + solutionPerformance.measure("OutputFilesWrite", "beforeOutputFilesWrite", "afterOutputFilesWrite"); finishEmit( emitterDiagnostics, @@ -1179,6 +1190,18 @@ namespace ts { state: SolutionBuilderState, buildOrder: AnyBuildOrder, reportQueue: boolean + ): InvalidateProjectCreateInfo | undefined { + solutionPerformance.mark("beforeGetNextInvalidatedProjectCreateInfo"); + const result = getNextInvalidatedProjectCreateInfoWorker(state, buildOrder, reportQueue); + solutionPerformance.mark("afterGetNextInvalidatedProjectCreateInfo"); + solutionPerformance.measure("GetNextInvalidatedProjectCreateInfo", "beforeGetNextInvalidatedProjectCreateInfo", "afterGetNextInvalidatedProjectCreateInfo"); + return result; + } + + function getNextInvalidatedProjectCreateInfoWorker( + state: SolutionBuilderState, + buildOrder: AnyBuildOrder, + reportQueue: boolean ): InvalidateProjectCreateInfo | undefined { if (!state.projectPendingBuild.size) return undefined; if (isCircularBuildOrder(buildOrder)) return undefined; @@ -1315,8 +1338,7 @@ namespace ts { reportQueue: boolean ): InvalidatedProject | undefined { const info = getNextInvalidatedProjectCreateInfo(state, buildOrder, reportQueue); - if (!info) return info; - return createInvalidatedProjectWithInfo(state, info, buildOrder); + return info && createInvalidatedProjectWithInfo(state, info, buildOrder); } function listEmittedFile({ write }: SolutionBuilderState, proj: ParsedCommandLine, file: string) { @@ -1325,11 +1347,15 @@ namespace ts { } } - function getOldProgram({ options, builderPrograms, compilerHost }: SolutionBuilderState, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) { - if (options.force) return undefined; - const value = builderPrograms.get(proj); + function getOldProgram(state: SolutionBuilderState, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) { + if (state.options.force) return undefined; + const value = state.builderPrograms.get(proj); if (value) return value; - return readBuilderProgram(parsed.options, compilerHost) as any as T; + solutionPerformance.mark("beforeReadBuilderProgram"); + const program = readBuilderProgram(parsed.options, state.compilerHost) as any as T; + solutionPerformance.mark("afterReadBuilderProgram"); + solutionPerformance.measure("ReadBuilderProgram", "beforeReadBuilderProgram", "afterReadBuilderProgram"); + return program; } function afterProgramDone( @@ -1557,7 +1583,10 @@ namespace ts { state.buildInfoChecked.set(resolvedPath, true); const buildInfoPath = getTsBuildInfoEmitOutputFilePath(project.options); if (buildInfoPath) { + solutionPerformance.mark("beforeBuildInfoRead"); const value = state.readFileWithCache(buildInfoPath); + solutionPerformance.mark("afterBuildInfoRead"); + solutionPerformance.measure("BuildInfoRead", "beforeBuildInfoRead", "afterBuildInfoRead"); const buildInfo = value && getBuildInfo(value); if (buildInfo && (buildInfo.bundle || buildInfo.program) && buildInfo.version !== version) { return { @@ -1598,13 +1627,17 @@ namespace ts { return prior; } + solutionPerformance.mark("beforeGetUpToDateStatus"); const actual = getUpToDateStatusWorker(state, project, resolvedPath); + solutionPerformance.mark("afterGetUpToDateStatus"); + solutionPerformance.measure("GetUpToDateStatus", "beforeGetUpToDateStatus", "afterGetUpToDateStatus"); state.projectStatus.set(resolvedPath, actual); return actual; } function updateOutputTimestampsWorker(state: SolutionBuilderState, proj: ParsedCommandLine, priorNewestUpdateTime: Date, verboseMessage: DiagnosticMessage, skipOutputs?: ESMap) { if (proj.options.noEmit) return priorNewestUpdateTime; + solutionPerformance.mark("beforeUpdateOutputTimestamps"); const { host } = state; const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames()); if (!skipOutputs || outputs.length !== skipOutputs.size) { @@ -1628,6 +1661,8 @@ namespace ts { } } + solutionPerformance.mark("afterUpdateOutputTimestamps"); + solutionPerformance.measure("UpdateOutputTimestamps", "beforeUpdateOutputTimestamps", "afterUpdateOutputTimestamps"); return priorNewestUpdateTime; } @@ -1687,7 +1722,7 @@ namespace ts { } break; } - // falls through + // falls through case UpToDateStatusType.UpToDateWithUpstreamTypes: case UpToDateStatusType.OutOfDateWithPrepend: @@ -1714,6 +1749,14 @@ namespace ts { } function build(state: SolutionBuilderState, project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers, onlyReferences?: boolean): ExitStatus { + solutionPerformance.mark("beforeBuild"); + const result = buildWorker(state, project, cancellationToken, writeFile, getCustomTransformers, onlyReferences); + solutionPerformance.mark("afterBuild"); + solutionPerformance.measure("Build", "beforeBuild", "afterBuild"); + return result; + } + + function buildWorker(state: SolutionBuilderState, project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers, onlyReferences?: boolean): ExitStatus { const buildOrder = getBuildOrderFor(state, project, onlyReferences); if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped; @@ -1725,7 +1768,10 @@ namespace ts { const invalidatedProject = getNextInvalidatedProject(state, buildOrder, reportQueue); if (!invalidatedProject) break; reportQueue = false; + solutionPerformance.mark("beforeInvalidatedProjectBuild"); invalidatedProject.done(cancellationToken, writeFile, getCustomTransformers?.(invalidatedProject.project)); + solutionPerformance.mark("afterInvalidatedProjectBuild"); + solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); if (!state.diagnostics.has(invalidatedProject.projectPath)) successfulProjects++; } @@ -1819,6 +1865,14 @@ namespace ts { } function buildNextInvalidatedProject(state: SolutionBuilderState, changeDetected: boolean) { + solutionPerformance.mark("beforeBuild"); + const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); + solutionPerformance.mark("afterBuild"); + solutionPerformance.measure("Build", "beforeBuild", "afterBuild"); + if (buildOrder) reportErrorSummary(state, buildOrder); + } + + function buildNextInvalidatedProjectWorker(state: SolutionBuilderState, changeDetected: boolean) { state.timerToBuildInvalidatedProject = undefined; if (state.reportFileChangeDetected) { state.reportFileChangeDetected = false; @@ -1829,7 +1883,10 @@ namespace ts { const buildOrder = getBuildOrder(state); const invalidatedProject = getNextInvalidatedProject(state, buildOrder, /*reportQueue*/ false); if (invalidatedProject) { + solutionPerformance.mark("beforeInvalidatedProjectBuild"); invalidatedProject.done(); + solutionPerformance.mark("afterInvalidatedProjectBuild"); + solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); projectsBuilt++; while (state.projectPendingBuild.size) { // If already scheduled, skip @@ -1843,12 +1900,15 @@ namespace ts { return; } const project = createInvalidatedProjectWithInfo(state, info, buildOrder); + solutionPerformance.mark("beforeInvalidatedProjectBuild"); project.done(); + solutionPerformance.mark("afterInvalidatedProjectBuild"); + solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); if (info.kind !== InvalidatedProjectKind.UpdateOutputFileStamps) projectsBuilt++; } } disableCache(state); - reportErrorSummary(state, buildOrder); + return buildOrder; } function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { @@ -1954,6 +2014,7 @@ namespace ts { function startWatching(state: SolutionBuilderState, buildOrder: AnyBuildOrder) { if (!state.watchAllProjectsPending) return; + solutionPerformance.mark("beforeStartWatching"); state.watchAllProjectsPending = false; for (const resolved of getBuildOrderFromAnyBuildOrder(buildOrder)) { const resolvedPath = toResolvedConfigFilePath(state, resolved); @@ -1972,6 +2033,8 @@ namespace ts { watchPackageJsonFiles(state, resolved, resolvedPath, cfg); } } + solutionPerformance.mark("afterStartWatching"); + solutionPerformance.measure("StartWatching", "beforeStartWatching", "afterStartWatching"); } function stopWatching(state: SolutionBuilderState) { diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 68d2e51e9d4eb..08d758da78d29 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -1,7 +1,18 @@ namespace ts { - interface Statistic { + export interface Statistic { name: string; - value: string; + value: number; + type: StatisticType + } + + export enum StatisticType { + time, + count, + memory, + } + + export interface SolutionBuilderHostBase extends ProgramHost { + statistics?: Statistic[][]; } function countLines(program: Program): Map { @@ -751,9 +762,22 @@ namespace ts { createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), createWatchStatusReporter(sys, buildOptions) ); + const onWatchStatusChange = buildHost.onWatchStatusChange; + let reportWatchStatistics = false; + buildHost.onWatchStatusChange = (d, newLine, options, errorCount) => { + onWatchStatusChange?.(d, newLine, options, errorCount); + if (!reportWatchStatistics) return; + if (d.code === Diagnostics.Found_0_errors_Watching_for_file_changes.code || + d.code === Diagnostics.Found_1_error_Watching_for_file_changes.code) { + reportSolutionBuilderTimes(sys, builder, buildHost); + } + }; updateSolutionBuilderHost(sys, cb, buildHost); + enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions, watchOptions); builder.build(); + reportSolutionBuilderTimes(sys, builder, buildHost); + reportWatchStatistics = true; return builder; } @@ -765,12 +789,55 @@ namespace ts { createReportErrorSummary(sys, buildOptions) ); updateSolutionBuilderHost(sys, cb, buildHost); + enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilder(buildHost, projects, buildOptions); const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); + reportSolutionBuilderTimes(sys, builder, buildHost); dumpTracingLegend(); // Will no-op if there hasn't been any tracing return sys.exit(exitStatus); } + function enableSolutionPerformance(system: System, options: BuildOptions) { + if (system === sys && (options.diagnostics || options.extendedDiagnostics)) solutionPerformance.enable(); + } + + function reportSolutionBuilderTimes(system: System, builder: SolutionBuilder, buildHost: SolutionBuilderHost) { + if (system !== sys) return; + + if (solutionPerformance.isEnabled()) { + const solutionStatistics: Statistic[] = []; + solutionPerformance.forEachMeasure((name, duration) => solutionStatistics.push({ name: `${name} time`, value: duration, type: StatisticType.time })); + solutionStatistics.push( + { name: "projectsBuilt", value: solutionPerformance.getCount("projectsBuilt"), type: StatisticType.count }, + { name: "timestampUpdated", value: solutionPerformance.getCount("timestampUpdated"), type: StatisticType.count }, + { name: "bundlesUpdated", value: solutionPerformance.getCount("bundlesUpdated"), type: StatisticType.count }, + { name: "projects", value: getBuildOrderFromAnyBuildOrder(builder.getBuildOrder()).length, type: StatisticType.count }, + ); + buildHost.statistics = append(buildHost.statistics, solutionStatistics); + solutionPerformance.disable(); + solutionPerformance.enable(); + } + + if (!buildHost.statistics) return; + const statistics: Statistic[] = []; + const map: Map = new Map(); + for (const statistic of buildHost.statistics) { + for (const s of statistic) { + const existing = map.get(s.name); + if (existing) { + if (existing.type === StatisticType.memory) existing.value = Math.max(existing.value, s.value); + else existing.value += s.value; + } + else { + map.set(s.name, s); + statistics.push(s); + } + } + } + buildHost.statistics = undefined; + reportAllStatistics(system, statistics); + } + function createReportErrorSummary(sys: System, options: CompilerOptions | BuildOptions): ReportEmitErrorSummary | undefined { return shouldBePretty(sys, options) ? (errorCount, filesInError) => sys.write(getErrorSummaryText(errorCount, filesInError, sys.newLine, sys)) : @@ -842,10 +909,13 @@ namespace ts { ) { updateCreateProgram(sys, buildHost); buildHost.afterProgramEmitAndDiagnostics = program => { - reportStatistics(sys, program.getProgram()); + buildHost.statistics = append(buildHost.statistics, reportStatistics(sys, program.getProgram())); cb(program); }; - buildHost.afterEmitBundle = cb; + buildHost.afterEmitBundle = config => { + buildHost.statistics = append(buildHost.statistics, reportStatistics(sys, config)); + cb(config); + }; } function updateCreateProgram(sys: System, host: { createProgram: CreateProgram; }) { @@ -939,8 +1009,13 @@ namespace ts { } } - function reportStatistics(sys: System, program: Program) { - const compilerOptions = program.getCompilerOptions(); + function isProgram(programOrConfig: Program | ParsedCommandLine): programOrConfig is Program { + return !!(programOrConfig as Program).getCompilerOptions; + } + function reportStatistics(sys: System, programOrConfig: Program | ParsedCommandLine): Statistic[] | undefined { + const program = isProgram(programOrConfig) ? programOrConfig : undefined; + const config = !isProgram(programOrConfig) ? programOrConfig : undefined; + const compilerOptions = program?.getCompilerOptions() || config?.options!; if (canTrace(sys, compilerOptions)) { tracing?.stopTracing(); @@ -950,30 +1025,32 @@ namespace ts { if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; - reportCountStatistic("Files", program.getSourceFiles().length); - - const lineCounts = countLines(program); - const nodeCounts = countNodes(program); - if (compilerOptions.extendedDiagnostics) { - for (const key of arrayFrom(lineCounts.keys())) { - reportCountStatistic("Lines of " + key, lineCounts.get(key)!); + if (program) { + reportCountStatistic("Files", program.getSourceFiles().length); + + const lineCounts = countLines(program); + const nodeCounts = countNodes(program); + if (compilerOptions.extendedDiagnostics) { + for (const key of arrayFrom(lineCounts.keys())) { + reportCountStatistic("Lines of " + key, lineCounts.get(key)!); + } + for (const key of arrayFrom(nodeCounts.keys())) { + reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!); + } } - for (const key of arrayFrom(nodeCounts.keys())) { - reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!); + else { + reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0)); + reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0)); } - } - else { - reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0)); - reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0)); - } - reportCountStatistic("Identifiers", program.getIdentifierCount()); - reportCountStatistic("Symbols", program.getSymbolCount()); - reportCountStatistic("Types", program.getTypeCount()); - reportCountStatistic("Instantiations", program.getInstantiationCount()); + reportCountStatistic("Identifiers", program.getIdentifierCount()); + reportCountStatistic("Symbols", program.getSymbolCount()); + reportCountStatistic("Types", program.getTypeCount()); + reportCountStatistic("Instantiations", program.getInstantiationCount()); + } if (memoryUsed >= 0) { - reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K"); + reportMemoryStatistic("Memory used", memoryUsed); } const isPerformanceEnabled = performance.isEnabled(); @@ -982,11 +1059,13 @@ namespace ts { const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0; const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; if (compilerOptions.extendedDiagnostics) { - const caches = program.getRelationCacheSizes(); - reportCountStatistic("Assignability cache size", caches.assignable); - reportCountStatistic("Identity cache size", caches.identity); - reportCountStatistic("Subtype cache size", caches.subtype); - reportCountStatistic("Strict subtype cache size", caches.strictSubtype); + if (program) { + const caches = program.getRelationCacheSizes(); + reportCountStatistic("Assignability cache size", caches.assignable); + reportCountStatistic("Identity cache size", caches.identity); + reportCountStatistic("Subtype cache size", caches.subtype); + reportCountStatistic("Strict subtype cache size", caches.strictSubtype); + } if (isPerformanceEnabled) { performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); } @@ -1006,43 +1085,59 @@ namespace ts { if (isPerformanceEnabled) { reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); } - reportStatistics(); + reportAllStatistics(sys, statistics); if (!isPerformanceEnabled) { sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); } else { performance.disable(); } + return statistics; } - function reportStatistics() { - let nameSize = 0; - let valueSize = 0; - for (const { name, value } of statistics) { - if (name.length > nameSize) { - nameSize = name.length; - } + function reportMemoryStatistic(name: string, memoryUsed: number) { + statistics.push({ name, value: memoryUsed, type: StatisticType.memory }); + } - if (value.length > valueSize) { - valueSize = value.length; - } - } + function reportCountStatistic(name: string, count: number) { + statistics.push({ name, value: count, type: StatisticType.count }); + } - for (const { name, value } of statistics) { - sys.write(padRight(name + ":", nameSize + 2) + padLeft(value.toString(), valueSize) + sys.newLine); - } + function reportTimeStatistic(name: string, time: number) { + statistics.push({ name, value: time, type: StatisticType.time }); } + } + + function reportAllStatistics(sys: System, statistics: Statistic[]) { + let nameSize = 0; + let valueSize = 0; + for (const s of statistics) { + if (s.name.length > nameSize) { + nameSize = s.name.length; + } - function reportStatisticalValue(name: string, value: string) { - statistics.push({ name, value }); + const valueString = statisticValue(s); + if (valueString.length > valueSize) { + valueSize = valueString.length; + } } - function reportCountStatistic(name: string, count: number) { - reportStatisticalValue(name, "" + count); + for (const s of statistics) { + sys.write(padRight(s.name + ":", nameSize + 2) + padLeft(statisticValue(s).toString(), valueSize) + sys.newLine); } + } - function reportTimeStatistic(name: string, time: number) { - reportStatisticalValue(name, (time / 1000).toFixed(2) + "s"); + function statisticValue(s: Statistic) { + switch (s.type) { + case StatisticType.count: + return "" + s.value; + case StatisticType.time: + return (s.value / 1000).toFixed(2) + "s"; + case StatisticType.memory: + return Math.round(s.value / 1000) + "K"; + break; + default: + Debug.assertNever(s.type); } } From d0fe79617461134498ea730fb3a8ab715e750c4a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 1 Jun 2022 11:33:12 -0700 Subject: [PATCH 04/10] Options solutionDiagnostics instead so that its not too verbose when printing diagnostics --- src/compiler/commandLineParser.ts | 9 ++++- src/compiler/diagnosticMessages.json | 6 ++- src/compiler/emitter.ts | 6 +-- src/compiler/performance.ts | 12 ++++++ src/compiler/tsbuildPublic.ts | 12 +++++- src/compiler/types.ts | 1 + src/compiler/watch.ts | 1 + src/executeCommandLine/executeCommandLine.ts | 40 ++++++++++---------- 8 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 09705e74f26eb..72ba973ee2b20 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1331,7 +1331,14 @@ namespace ts { description: Diagnostics.Delete_the_outputs_of_all_projects, type: "boolean", defaultValueDescription: false, - } + }, + { + name: "solutionDiagnostics", + type: "boolean", + category: Diagnostics.Compiler_Diagnostics, + description: Diagnostics.Output_more_detailed_solution_performance_information_after_building, + defaultValueDescription: false, + }, ]; /* @internal */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 05717148ebc33..863e83acda040 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5261,7 +5261,7 @@ "code": 6385, "reportsDeprecated": true }, - "Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { + "Performance timings for '--diagnostics' or '--extendedDiagnostics' or '--solutionDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { "category": "Message", "code": 6386 }, @@ -5799,6 +5799,10 @@ "category": "Message", "code": 6718 }, + "Output more detailed solution performance information after building.": { + "category": "Message", + "code": 6719 + }, "Default catch clause variables as 'unknown' instead of 'any'.": { "category": "Message", "code": 6803 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c83713750f2a6..82c2ead6848b5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -395,7 +395,7 @@ namespace ts { sourceMap: compilerOptions.sourceMap, inlineSourceMap: compilerOptions.inlineSourceMap, inlineSources: compilerOptions.inlineSources, - extendedDiagnostics: compilerOptions.extendedDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, writeBundleFileInfo: !!bundleBuildInfo, relativeToBuildInfo }; @@ -453,7 +453,7 @@ namespace ts { target: compilerOptions.target, sourceMap: compilerOptions.sourceMap, inlineSourceMap: compilerOptions.inlineSourceMap, - extendedDiagnostics: compilerOptions.extendedDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, onlyPrintJsDocStyle: true, writeBundleFileInfo: !!bundleBuildInfo, recordInternalSection: !!bundleBuildInfo, @@ -482,7 +482,7 @@ namespace ts { sourceMap: !forceDtsEmit && compilerOptions.declarationMap, sourceRoot: compilerOptions.sourceRoot, mapRoot: compilerOptions.mapRoot, - extendedDiagnostics: compilerOptions.extendedDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, // Explicitly do not passthru either `inline` option } ); diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 2dbdb390b4078..9f6ca1a9dcc19 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -20,6 +20,7 @@ namespace ts { const marks = new Map(); const counts = new Map(); const durations = new Map(); + const durationMarks = new Set(); return { createTimerIf, @@ -29,6 +30,7 @@ namespace ts { getCount, getDuration, forEachMeasure, + forEachCount, isEnabled, enable, disable, @@ -87,6 +89,7 @@ namespace ts { */ function measure(measureName: string, startMarkName: string, endMarkName: string) { if (enabled) { + durationMarks.add(startMarkName).add(endMarkName); const end = marks.get(endMarkName) ?? timestamp(); const start = marks.get(startMarkName) ?? timeorigin; const previousDuration = durations.get(measureName) || 0; @@ -122,6 +125,15 @@ namespace ts { durations.forEach((duration, measureName) => cb(measureName, duration)); } + /** + * Iterate over each count which is not duration mark, performing some action + * + * @param cb The action to perform for each measure + */ + function forEachCount(cb: (countName: string, count: number) => void) { + counts.forEach((count, countName) => !durationMarks.has(countName) && cb(countName, count)); + } + /** * Indicates whether the performance API is enabled. */ diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 1dc941d5aa894..d929e24c67601 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -22,6 +22,7 @@ namespace ts { traceResolution?: boolean; /* @internal */ diagnostics?: boolean; /* @internal */ extendedDiagnostics?: boolean; + /* @internal */ solutionDiagnostics?: boolean; /* @internal */ locale?: string; /* @internal */ generateCpuProfile?: string; /* @internal */ generateTrace?: string; @@ -188,6 +189,7 @@ namespace ts { commonOptionsWithBuild.forEach(option => { if (hasProperty(buildOptions, option.name)) result[option.name] = buildOptions[option.name]; }); + if (buildOptions.solutionDiagnostics) result.solutionDiagnostics = true; return result; } @@ -1788,7 +1790,15 @@ namespace ts { : ExitStatus.DiagnosticsPresent_OutputsSkipped; } - function clean(state: SolutionBuilderState, project?: string, onlyReferences?: boolean) { + function clean(state: SolutionBuilderState, project?: string, onlyReferences?: boolean): ExitStatus { + solutionPerformance.mark("beforeClean"); + const result = cleanWorker(state, project, onlyReferences); + solutionPerformance.mark("afterClean"); + solutionPerformance.measure("Clean", "beforeClean", "afterClean"); + return result; + } + + function cleanWorker(state: SolutionBuilderState, project?: string, onlyReferences?: boolean) { const buildOrder = getBuildOrderFor(state, project, onlyReferences); if (!buildOrder) return ExitStatus.InvalidProject_OutputsSkipped; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5aca9584a99a4..fec7f35399877 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6148,6 +6148,7 @@ namespace ts { declarationDir?: string; /* @internal */ diagnostics?: boolean; /* @internal */ extendedDiagnostics?: boolean; + /* @internal */ solutionDiagnostics?: boolean; disableSizeLimit?: boolean; disableSourceOfProjectReferenceRedirect?: boolean; disableSolutionSearching?: boolean; diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 7d2c6818d33c7..7705e7cf037f6 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -34,6 +34,7 @@ namespace ts { if (system.clearScreen && !options.preserveWatchOutput && !options.extendedDiagnostics && + !options.solutionDiagnostics && !options.diagnostics && contains(screenStartingMessageCodes, diagnostic.code)) { system.clearScreen(); diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 08d758da78d29..eda751143f86d 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -769,14 +769,14 @@ namespace ts { if (!reportWatchStatistics) return; if (d.code === Diagnostics.Found_0_errors_Watching_for_file_changes.code || d.code === Diagnostics.Found_1_error_Watching_for_file_changes.code) { - reportSolutionBuilderTimes(sys, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); } }; updateSolutionBuilderHost(sys, cb, buildHost); enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions, watchOptions); builder.build(); - reportSolutionBuilderTimes(sys, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); reportWatchStatistics = true; return builder; } @@ -792,31 +792,33 @@ namespace ts { enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilder(buildHost, projects, buildOptions); const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); - reportSolutionBuilderTimes(sys, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); dumpTracingLegend(); // Will no-op if there hasn't been any tracing return sys.exit(exitStatus); } function enableSolutionPerformance(system: System, options: BuildOptions) { - if (system === sys && (options.diagnostics || options.extendedDiagnostics)) solutionPerformance.enable(); + if (system === sys && options.solutionDiagnostics) solutionPerformance.enable(); } - function reportSolutionBuilderTimes(system: System, builder: SolutionBuilder, buildHost: SolutionBuilderHost) { - if (system !== sys) return; + function reportSolutionBuilderTimes(system: System, buildOptions: BuildOptions, builder: SolutionBuilder, buildHost: SolutionBuilderHost) { + if (system !== sys || !buildOptions.solutionDiagnostics) return; + if (solutionPerformance.isEnabled()) { const solutionStatistics: Statistic[] = []; - solutionPerformance.forEachMeasure((name, duration) => solutionStatistics.push({ name: `${name} time`, value: duration, type: StatisticType.time })); solutionStatistics.push( - { name: "projectsBuilt", value: solutionPerformance.getCount("projectsBuilt"), type: StatisticType.count }, - { name: "timestampUpdated", value: solutionPerformance.getCount("timestampUpdated"), type: StatisticType.count }, - { name: "bundlesUpdated", value: solutionPerformance.getCount("bundlesUpdated"), type: StatisticType.count }, - { name: "projects", value: getBuildOrderFromAnyBuildOrder(builder.getBuildOrder()).length, type: StatisticType.count }, + { name: "Projects", value: getBuildOrderFromAnyBuildOrder(builder.getBuildOrder()).length, type: StatisticType.count }, ); + solutionPerformance.forEachCount((name, count) => solutionStatistics.push({ name, value: count, type: StatisticType.count })); + solutionPerformance.forEachMeasure((name, duration) => solutionStatistics.push({ name: `${name} time`, value: duration, type: StatisticType.time })); buildHost.statistics = append(buildHost.statistics, solutionStatistics); solutionPerformance.disable(); solutionPerformance.enable(); } + else { + sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_or_solutionDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + } if (!buildHost.statistics) return; const statistics: Statistic[] = []; @@ -990,8 +992,8 @@ namespace ts { return createWatchProgram(watchCompilerHost); } - function canReportDiagnostics(system: System, compilerOptions: CompilerOptions) { - return system === sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics); + function canGenerateStatistics(system: System, compilerOptions: CompilerOptions) { + return system === sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics); } function canTrace(system: System, compilerOptions: CompilerOptions) { @@ -999,7 +1001,7 @@ namespace ts { } function enableStatisticsAndTracing(system: System, compilerOptions: CompilerOptions, isBuildMode: boolean) { - if (canReportDiagnostics(system, compilerOptions)) { + if (canGenerateStatistics(system, compilerOptions)) { performance.enable(system); } @@ -1022,7 +1024,7 @@ namespace ts { } let statistics: Statistic[]; - if (canReportDiagnostics(sys, compilerOptions)) { + if (canGenerateStatistics(sys, compilerOptions)) { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; if (program) { @@ -1030,7 +1032,7 @@ namespace ts { const lineCounts = countLines(program); const nodeCounts = countNodes(program); - if (compilerOptions.extendedDiagnostics) { + if (compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics) { for (const key of arrayFrom(lineCounts.keys())) { reportCountStatistic("Lines of " + key, lineCounts.get(key)!); } @@ -1058,7 +1060,7 @@ namespace ts { const bindTime = isPerformanceEnabled ? performance.getDuration("Bind") : 0; const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0; const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; - if (compilerOptions.extendedDiagnostics) { + if (compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics) { if (program) { const caches = program.getRelationCacheSizes(); reportCountStatistic("Assignability cache size", caches.assignable); @@ -1085,9 +1087,9 @@ namespace ts { if (isPerformanceEnabled) { reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); } - reportAllStatistics(sys, statistics); + if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) reportAllStatistics(sys, statistics); if (!isPerformanceEnabled) { - sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_or_solutionDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); } else { performance.disable(); From 6fc09f84d89d6a362e9889952e168d80d1926192 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Jun 2022 11:34:44 -0700 Subject: [PATCH 05/10] Some of the cleanup per feedback --- src/compiler/commandLineParser.ts | 9 +- src/compiler/diagnosticMessages.json | 6 +- src/compiler/emitter.ts | 6 +- src/compiler/performance.ts | 45 ++++++- src/compiler/tsbuildPublic.ts | 82 +++--------- src/compiler/types.ts | 1 - src/compiler/watch.ts | 1 - src/executeCommandLine/executeCommandLine.ts | 132 +++++++------------ 8 files changed, 116 insertions(+), 166 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 72ba973ee2b20..09705e74f26eb 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1331,14 +1331,7 @@ namespace ts { description: Diagnostics.Delete_the_outputs_of_all_projects, type: "boolean", defaultValueDescription: false, - }, - { - name: "solutionDiagnostics", - type: "boolean", - category: Diagnostics.Compiler_Diagnostics, - description: Diagnostics.Output_more_detailed_solution_performance_information_after_building, - defaultValueDescription: false, - }, + } ]; /* @internal */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 863e83acda040..05717148ebc33 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5261,7 +5261,7 @@ "code": 6385, "reportsDeprecated": true }, - "Performance timings for '--diagnostics' or '--extendedDiagnostics' or '--solutionDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { + "Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { "category": "Message", "code": 6386 }, @@ -5799,10 +5799,6 @@ "category": "Message", "code": 6718 }, - "Output more detailed solution performance information after building.": { - "category": "Message", - "code": 6719 - }, "Default catch clause variables as 'unknown' instead of 'any'.": { "category": "Message", "code": 6803 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 82c2ead6848b5..c83713750f2a6 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -395,7 +395,7 @@ namespace ts { sourceMap: compilerOptions.sourceMap, inlineSourceMap: compilerOptions.inlineSourceMap, inlineSources: compilerOptions.inlineSources, - extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics, writeBundleFileInfo: !!bundleBuildInfo, relativeToBuildInfo }; @@ -453,7 +453,7 @@ namespace ts { target: compilerOptions.target, sourceMap: compilerOptions.sourceMap, inlineSourceMap: compilerOptions.inlineSourceMap, - extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics, onlyPrintJsDocStyle: true, writeBundleFileInfo: !!bundleBuildInfo, recordInternalSection: !!bundleBuildInfo, @@ -482,7 +482,7 @@ namespace ts { sourceMap: !forceDtsEmit && compilerOptions.declarationMap, sourceRoot: compilerOptions.sourceRoot, mapRoot: compilerOptions.mapRoot, - extendedDiagnostics: compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics, + extendedDiagnostics: compilerOptions.extendedDiagnostics, // Explicitly do not passthru either `inline` option } ); diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 9f6ca1a9dcc19..73493289f96fa 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -6,9 +6,21 @@ namespace ts { exit(): void; } + export interface Statistic { + name: string; + value: number; + type: StatisticType + } + + export enum StatisticType { + time, + count, + memory, + } + const nullTimer: Timer = { enter: noop, exit: noop }; export const performance = createPerformanceTracker(); - export const solutionPerformance = createPerformanceTracker(); + export const buildPerformance = createPerformanceTracker(); function createPerformanceTracker() { let perfHooks: PerformanceHooks | undefined; @@ -21,16 +33,19 @@ namespace ts { const counts = new Map(); const durations = new Map(); const durationMarks = new Set(); + let statistics: ESMap | undefined; return { createTimerIf, createTimer, mark, measure, + addStatistics, getCount, getDuration, forEachMeasure, forEachCount, + forEachStatistics, isEnabled, enable, disable, @@ -98,6 +113,19 @@ namespace ts { } } + function addStatistics(s: Statistic) { + if (enabled) { + const existing = statistics?.get(s.name); + if (existing) { + if (existing.type === StatisticType.memory) existing.value = Math.max(existing.value, s.value); + else existing.value += s.value; + } + else { + (statistics ??= new Map()).set(s.name, s); + } + } + } + /** * Gets the number of times a marker was encountered. * @@ -121,8 +149,8 @@ namespace ts { * * @param cb The action to perform for each measure */ - function forEachMeasure(cb: (measureName: string, duration: number) => void) { - durations.forEach((duration, measureName) => cb(measureName, duration)); + function forEachMeasure(cb: (duration: number, measureName: string) => void) { + durations.forEach(cb); } /** @@ -130,8 +158,13 @@ namespace ts { * * @param cb The action to perform for each measure */ - function forEachCount(cb: (countName: string, count: number) => void) { - counts.forEach((count, countName) => !durationMarks.has(countName) && cb(countName, count)); + function forEachCount(cb: (count: number, countName: string) => void) { + counts.forEach((count, countName) => !durationMarks.has(countName) && cb(count, countName)); + } + + + function forEachStatistics(cb: (statistic: Statistic, name: string) => void) { + statistics?.forEach(cb); } /** @@ -166,6 +199,8 @@ namespace ts { marks.clear(); counts.clear(); durations.clear(); + durationMarks.clear(); + statistics?.clear(); performanceImpl = undefined; enabled = false; } diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index d929e24c67601..5af90aafc5253 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -22,7 +22,6 @@ namespace ts { traceResolution?: boolean; /* @internal */ diagnostics?: boolean; /* @internal */ extendedDiagnostics?: boolean; - /* @internal */ solutionDiagnostics?: boolean; /* @internal */ locale?: string; /* @internal */ generateCpuProfile?: string; /* @internal */ generateTrace?: string; @@ -189,7 +188,6 @@ namespace ts { commonOptionsWithBuild.forEach(option => { if (hasProperty(buildOptions, option.name)) result[option.name] = buildOptions[option.name]; }); - if (buildOptions.solutionDiagnostics) result.solutionDiagnostics = true; return result; } @@ -380,7 +378,7 @@ namespace ts { if (value) { return isParsedCommandLine(value) ? value : undefined; } - solutionPerformance.mark("beforeParseConfigFile"); + buildPerformance.mark("beforeConfigFileParsing"); let diagnostic: Diagnostic | undefined; const { parseConfigFileHost, baseCompilerOptions, baseWatchOptions, extendedConfigCache, host } = state; let parsed: ParsedCommandLine | undefined; @@ -394,8 +392,8 @@ namespace ts { parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop; } configFileCache.set(configFilePath, parsed || diagnostic!); - solutionPerformance.mark("afterParseConfigFile"); - solutionPerformance.measure("ParseConfigFile", "beforeParseConfigFile", "afterParseConfigFile"); + buildPerformance.mark("afterConfigFileParsing"); + buildPerformance.measure("Config File Parsing", "beforeConfigFileParsing", "afterConfigFileParsing"); return parsed; } @@ -455,7 +453,6 @@ namespace ts { } function createStateBuildOrder(state: SolutionBuilderState) { - solutionPerformance.mark("beforeCreateBuildOrder"); const buildOrder = createBuildOrder(state, state.rootNames.map(f => resolveProjectName(state, f))); // Clear all to ResolvedConfigFilePaths cache to start fresh @@ -512,8 +509,6 @@ namespace ts { { onDeleteValue: existingMap => existingMap.forEach(closeFileWatcher) } ); } - solutionPerformance.mark("afterCreateBuildOrder"); - solutionPerformance.measure("CreateBuildOrder", "beforeCreateBuildOrder", "afterCreateBuildOrder"); return state.buildOrder = buildOrder; } @@ -715,7 +710,7 @@ namespace ts { if (updateOutputFileStampsPending) { updateOutputTimestamps(state, config, projectPath); } - solutionPerformance.mark("timestampUpdated"); + buildPerformance.mark("Projects With Only Timestamp Updates"); return doneInvalidatedProject(state, projectPath); } }; @@ -829,8 +824,8 @@ namespace ts { function done(cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) { executeSteps(BuildStep.Done, cancellationToken, writeFile, customTransformers); - if (kind === InvalidatedProjectKind.Build) solutionPerformance.mark("projectsBuilt"); - else solutionPerformance.mark("bundlesUpdated"); + if (kind === InvalidatedProjectKind.Build) buildPerformance.mark("Projects Built"); + else buildPerformance.mark("Bundles Updated"); return doneInvalidatedProject(state, projectPath); } @@ -971,7 +966,6 @@ namespace ts { let anyDtsChanged = false; const emitterDiagnostics = createDiagnosticCollection(); const emittedOutputs = new Map(); - solutionPerformance.mark("beforeOutputFilesWrite"); outputFiles.forEach(({ name, text, writeByteOrderMark }) => { let priorChangeTime: Date | undefined; if (!anyDtsChanged && isDeclarationFileName(name)) { @@ -991,8 +985,6 @@ namespace ts { newestDeclarationFileContentChangedTime = newer(priorChangeTime, newestDeclarationFileContentChangedTime); } }); - solutionPerformance.mark("afterOutputFilesWrite"); - solutionPerformance.measure("OutputFilesWrite", "beforeOutputFilesWrite", "afterOutputFilesWrite"); finishEmit( emitterDiagnostics, @@ -1193,11 +1185,7 @@ namespace ts { buildOrder: AnyBuildOrder, reportQueue: boolean ): InvalidateProjectCreateInfo | undefined { - solutionPerformance.mark("beforeGetNextInvalidatedProjectCreateInfo"); - const result = getNextInvalidatedProjectCreateInfoWorker(state, buildOrder, reportQueue); - solutionPerformance.mark("afterGetNextInvalidatedProjectCreateInfo"); - solutionPerformance.measure("GetNextInvalidatedProjectCreateInfo", "beforeGetNextInvalidatedProjectCreateInfo", "afterGetNextInvalidatedProjectCreateInfo"); - return result; + return getNextInvalidatedProjectCreateInfoWorker(state, buildOrder, reportQueue); } function getNextInvalidatedProjectCreateInfoWorker( @@ -1353,11 +1341,7 @@ namespace ts { if (state.options.force) return undefined; const value = state.builderPrograms.get(proj); if (value) return value; - solutionPerformance.mark("beforeReadBuilderProgram"); - const program = readBuilderProgram(parsed.options, state.compilerHost) as any as T; - solutionPerformance.mark("afterReadBuilderProgram"); - solutionPerformance.measure("ReadBuilderProgram", "beforeReadBuilderProgram", "afterReadBuilderProgram"); - return program; + return readBuilderProgram(parsed.options, state.compilerHost) as any as T; } function afterProgramDone( @@ -1585,10 +1569,7 @@ namespace ts { state.buildInfoChecked.set(resolvedPath, true); const buildInfoPath = getTsBuildInfoEmitOutputFilePath(project.options); if (buildInfoPath) { - solutionPerformance.mark("beforeBuildInfoRead"); const value = state.readFileWithCache(buildInfoPath); - solutionPerformance.mark("afterBuildInfoRead"); - solutionPerformance.measure("BuildInfoRead", "beforeBuildInfoRead", "afterBuildInfoRead"); const buildInfo = value && getBuildInfo(value); if (buildInfo && (buildInfo.bundle || buildInfo.program) && buildInfo.version !== version) { return { @@ -1629,17 +1610,16 @@ namespace ts { return prior; } - solutionPerformance.mark("beforeGetUpToDateStatus"); + buildPerformance.mark("beforeUpToDateCheckTime"); const actual = getUpToDateStatusWorker(state, project, resolvedPath); - solutionPerformance.mark("afterGetUpToDateStatus"); - solutionPerformance.measure("GetUpToDateStatus", "beforeGetUpToDateStatus", "afterGetUpToDateStatus"); + buildPerformance.mark("afterUpToDateCheckTime"); + buildPerformance.measure("Up-to-date check time", "beforeUpToDateCheckTime", "afterUpToDateCheckTime"); state.projectStatus.set(resolvedPath, actual); return actual; } function updateOutputTimestampsWorker(state: SolutionBuilderState, proj: ParsedCommandLine, priorNewestUpdateTime: Date, verboseMessage: DiagnosticMessage, skipOutputs?: ESMap) { if (proj.options.noEmit) return priorNewestUpdateTime; - solutionPerformance.mark("beforeUpdateOutputTimestamps"); const { host } = state; const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames()); if (!skipOutputs || outputs.length !== skipOutputs.size) { @@ -1662,9 +1642,6 @@ namespace ts { host.setModifiedTime(file, now); } } - - solutionPerformance.mark("afterUpdateOutputTimestamps"); - solutionPerformance.measure("UpdateOutputTimestamps", "beforeUpdateOutputTimestamps", "afterUpdateOutputTimestamps"); return priorNewestUpdateTime; } @@ -1751,10 +1728,10 @@ namespace ts { } function build(state: SolutionBuilderState, project?: string, cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, getCustomTransformers?: (project: string) => CustomTransformers, onlyReferences?: boolean): ExitStatus { - solutionPerformance.mark("beforeBuild"); + buildPerformance.mark("beforeBuild"); const result = buildWorker(state, project, cancellationToken, writeFile, getCustomTransformers, onlyReferences); - solutionPerformance.mark("afterBuild"); - solutionPerformance.measure("Build", "beforeBuild", "afterBuild"); + buildPerformance.mark("afterBuild"); + buildPerformance.measure("Build", "beforeBuild", "afterBuild"); return result; } @@ -1770,10 +1747,7 @@ namespace ts { const invalidatedProject = getNextInvalidatedProject(state, buildOrder, reportQueue); if (!invalidatedProject) break; reportQueue = false; - solutionPerformance.mark("beforeInvalidatedProjectBuild"); invalidatedProject.done(cancellationToken, writeFile, getCustomTransformers?.(invalidatedProject.project)); - solutionPerformance.mark("afterInvalidatedProjectBuild"); - solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); if (!state.diagnostics.has(invalidatedProject.projectPath)) successfulProjects++; } @@ -1791,10 +1765,10 @@ namespace ts { } function clean(state: SolutionBuilderState, project?: string, onlyReferences?: boolean): ExitStatus { - solutionPerformance.mark("beforeClean"); + buildPerformance.mark("beforeClean"); const result = cleanWorker(state, project, onlyReferences); - solutionPerformance.mark("afterClean"); - solutionPerformance.measure("Clean", "beforeClean", "afterClean"); + buildPerformance.mark("afterClean"); + buildPerformance.measure("Clean", "beforeClean", "afterClean"); return result; } @@ -1875,14 +1849,6 @@ namespace ts { } function buildNextInvalidatedProject(state: SolutionBuilderState, changeDetected: boolean) { - solutionPerformance.mark("beforeBuild"); - const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); - solutionPerformance.mark("afterBuild"); - solutionPerformance.measure("Build", "beforeBuild", "afterBuild"); - if (buildOrder) reportErrorSummary(state, buildOrder); - } - - function buildNextInvalidatedProjectWorker(state: SolutionBuilderState, changeDetected: boolean) { state.timerToBuildInvalidatedProject = undefined; if (state.reportFileChangeDetected) { state.reportFileChangeDetected = false; @@ -1893,10 +1859,7 @@ namespace ts { const buildOrder = getBuildOrder(state); const invalidatedProject = getNextInvalidatedProject(state, buildOrder, /*reportQueue*/ false); if (invalidatedProject) { - solutionPerformance.mark("beforeInvalidatedProjectBuild"); invalidatedProject.done(); - solutionPerformance.mark("afterInvalidatedProjectBuild"); - solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); projectsBuilt++; while (state.projectPendingBuild.size) { // If already scheduled, skip @@ -1910,15 +1873,12 @@ namespace ts { return; } const project = createInvalidatedProjectWithInfo(state, info, buildOrder); - solutionPerformance.mark("beforeInvalidatedProjectBuild"); project.done(); - solutionPerformance.mark("afterInvalidatedProjectBuild"); - solutionPerformance.measure("InvalidatedProjectBuild", "beforeInvalidatedProjectBuild", "afterInvalidatedProjectBuild"); if (info.kind !== InvalidatedProjectKind.UpdateOutputFileStamps) projectsBuilt++; } } disableCache(state); - return buildOrder; + reportErrorSummary(state, buildOrder); } function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { @@ -2024,7 +1984,7 @@ namespace ts { function startWatching(state: SolutionBuilderState, buildOrder: AnyBuildOrder) { if (!state.watchAllProjectsPending) return; - solutionPerformance.mark("beforeStartWatching"); + buildPerformance.mark("beforeWatcherCreation"); state.watchAllProjectsPending = false; for (const resolved of getBuildOrderFromAnyBuildOrder(buildOrder)) { const resolvedPath = toResolvedConfigFilePath(state, resolved); @@ -2043,8 +2003,8 @@ namespace ts { watchPackageJsonFiles(state, resolved, resolvedPath, cfg); } } - solutionPerformance.mark("afterStartWatching"); - solutionPerformance.measure("StartWatching", "beforeStartWatching", "afterStartWatching"); + buildPerformance.mark("afterWatcherCreation"); + buildPerformance.measure("Watcher Creation", "beforeWatcherCreation", "afterWatcherCreation"); } function stopWatching(state: SolutionBuilderState) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fec7f35399877..5aca9584a99a4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6148,7 +6148,6 @@ namespace ts { declarationDir?: string; /* @internal */ diagnostics?: boolean; /* @internal */ extendedDiagnostics?: boolean; - /* @internal */ solutionDiagnostics?: boolean; disableSizeLimit?: boolean; disableSourceOfProjectReferenceRedirect?: boolean; disableSolutionSearching?: boolean; diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 7705e7cf037f6..7d2c6818d33c7 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -34,7 +34,6 @@ namespace ts { if (system.clearScreen && !options.preserveWatchOutput && !options.extendedDiagnostics && - !options.solutionDiagnostics && !options.diagnostics && contains(screenStartingMessageCodes, diagnostic.code)) { system.clearScreen(); diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index eda751143f86d..7b42459a78495 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -1,20 +1,4 @@ namespace ts { - export interface Statistic { - name: string; - value: number; - type: StatisticType - } - - export enum StatisticType { - time, - count, - memory, - } - - export interface SolutionBuilderHostBase extends ProgramHost { - statistics?: Statistic[][]; - } - function countLines(program: Program): Map { const counts = getCountsMap(); forEach(program.getSourceFiles(), file => { @@ -769,14 +753,14 @@ namespace ts { if (!reportWatchStatistics) return; if (d.code === Diagnostics.Found_0_errors_Watching_for_file_changes.code || d.code === Diagnostics.Found_1_error_Watching_for_file_changes.code) { - reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder); } }; updateSolutionBuilderHost(sys, cb, buildHost); enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions, watchOptions); builder.build(); - reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder); reportWatchStatistics = true; return builder; } @@ -792,51 +776,32 @@ namespace ts { enableSolutionPerformance(sys, buildOptions); const builder = createSolutionBuilder(buildHost, projects, buildOptions); const exitStatus = buildOptions.clean ? builder.clean() : builder.build(); - reportSolutionBuilderTimes(sys, buildOptions, builder, buildHost); + reportSolutionBuilderTimes(sys, buildOptions, builder); dumpTracingLegend(); // Will no-op if there hasn't been any tracing return sys.exit(exitStatus); } function enableSolutionPerformance(system: System, options: BuildOptions) { - if (system === sys && options.solutionDiagnostics) solutionPerformance.enable(); + if (system === sys && options.extendedDiagnostics) buildPerformance.enable(); } - function reportSolutionBuilderTimes(system: System, buildOptions: BuildOptions, builder: SolutionBuilder, buildHost: SolutionBuilderHost) { - if (system !== sys || !buildOptions.solutionDiagnostics) return; - + function reportSolutionBuilderTimes(system: System, buildOptions: BuildOptions, builder: SolutionBuilder) { + if (system !== sys || !buildOptions.extendedDiagnostics) return; - if (solutionPerformance.isEnabled()) { - const solutionStatistics: Statistic[] = []; - solutionStatistics.push( - { name: "Projects", value: getBuildOrderFromAnyBuildOrder(builder.getBuildOrder()).length, type: StatisticType.count }, - ); - solutionPerformance.forEachCount((name, count) => solutionStatistics.push({ name, value: count, type: StatisticType.count })); - solutionPerformance.forEachMeasure((name, duration) => solutionStatistics.push({ name: `${name} time`, value: duration, type: StatisticType.time })); - buildHost.statistics = append(buildHost.statistics, solutionStatistics); - solutionPerformance.disable(); - solutionPerformance.enable(); - } - else { - sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_or_solutionDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + if (!buildPerformance.isEnabled()) { + sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + return; } - if (!buildHost.statistics) return; const statistics: Statistic[] = []; - const map: Map = new Map(); - for (const statistic of buildHost.statistics) { - for (const s of statistic) { - const existing = map.get(s.name); - if (existing) { - if (existing.type === StatisticType.memory) existing.value = Math.max(existing.value, s.value); - else existing.value += s.value; - } - else { - map.set(s.name, s); - statistics.push(s); - } - } - } - buildHost.statistics = undefined; + statistics.push( + { name: "Projects in scope", value: getBuildOrderFromAnyBuildOrder(builder.getBuildOrder()).length, type: StatisticType.count }, + ); + buildPerformance.forEachCount((count, name) => statistics.push({ name, value: count, type: StatisticType.count })); + buildPerformance.forEachStatistics(s => statistics.push(s)); + buildPerformance.forEachMeasure((duration, name) => statistics.push({ name: `${name} time`, value: duration, type: StatisticType.time })); + buildPerformance.disable(); + buildPerformance.enable(); reportAllStatistics(system, statistics); } @@ -911,11 +876,11 @@ namespace ts { ) { updateCreateProgram(sys, buildHost); buildHost.afterProgramEmitAndDiagnostics = program => { - buildHost.statistics = append(buildHost.statistics, reportStatistics(sys, program.getProgram())); + reportStatistics(sys, program.getProgram()); cb(program); }; buildHost.afterEmitBundle = config => { - buildHost.statistics = append(buildHost.statistics, reportStatistics(sys, config)); + reportStatistics(sys, config); cb(config); }; } @@ -992,8 +957,8 @@ namespace ts { return createWatchProgram(watchCompilerHost); } - function canGenerateStatistics(system: System, compilerOptions: CompilerOptions) { - return system === sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics); + function canReportDiagnostics(system: System, compilerOptions: CompilerOptions) { + return system === sys && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics); } function canTrace(system: System, compilerOptions: CompilerOptions) { @@ -1001,7 +966,7 @@ namespace ts { } function enableStatisticsAndTracing(system: System, compilerOptions: CompilerOptions, isBuildMode: boolean) { - if (canGenerateStatistics(system, compilerOptions)) { + if (canReportDiagnostics(system, compilerOptions)) { performance.enable(system); } @@ -1014,7 +979,7 @@ namespace ts { function isProgram(programOrConfig: Program | ParsedCommandLine): programOrConfig is Program { return !!(programOrConfig as Program).getCompilerOptions; } - function reportStatistics(sys: System, programOrConfig: Program | ParsedCommandLine): Statistic[] | undefined { + function reportStatistics(sys: System, programOrConfig: Program | ParsedCommandLine) { const program = isProgram(programOrConfig) ? programOrConfig : undefined; const config = !isProgram(programOrConfig) ? programOrConfig : undefined; const compilerOptions = program?.getCompilerOptions() || config?.options!; @@ -1024,31 +989,31 @@ namespace ts { } let statistics: Statistic[]; - if (canGenerateStatistics(sys, compilerOptions)) { + if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; if (program) { - reportCountStatistic("Files", program.getSourceFiles().length); + reportCountStatistic("Files", program.getSourceFiles().length, /*aggregate*/ true); const lineCounts = countLines(program); const nodeCounts = countNodes(program); - if (compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics) { + if (compilerOptions.extendedDiagnostics) { for (const key of arrayFrom(lineCounts.keys())) { - reportCountStatistic("Lines of " + key, lineCounts.get(key)!); + reportCountStatistic("Lines of " + key, lineCounts.get(key)!, /*aggregate*/ true); } for (const key of arrayFrom(nodeCounts.keys())) { - reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!); + reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!, /*aggregate*/ false); } } else { - reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0)); - reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0)); + reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ true); + reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ false); } - reportCountStatistic("Identifiers", program.getIdentifierCount()); - reportCountStatistic("Symbols", program.getSymbolCount()); - reportCountStatistic("Types", program.getTypeCount()); - reportCountStatistic("Instantiations", program.getInstantiationCount()); + reportCountStatistic("Identifiers", program.getIdentifierCount(), /*aggregate*/ true); + reportCountStatistic("Symbols", program.getSymbolCount(), /*aggregate*/ true); + reportCountStatistic("Types", program.getTypeCount(), /*aggregate*/ true); + reportCountStatistic("Instantiations", program.getInstantiationCount(), /*aggregate*/ true); } if (memoryUsed >= 0) { @@ -1060,16 +1025,16 @@ namespace ts { const bindTime = isPerformanceEnabled ? performance.getDuration("Bind") : 0; const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0; const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; - if (compilerOptions.extendedDiagnostics || compilerOptions.solutionDiagnostics) { + if (compilerOptions.extendedDiagnostics) { if (program) { const caches = program.getRelationCacheSizes(); - reportCountStatistic("Assignability cache size", caches.assignable); - reportCountStatistic("Identity cache size", caches.identity); - reportCountStatistic("Subtype cache size", caches.subtype); - reportCountStatistic("Strict subtype cache size", caches.strictSubtype); + reportCountStatistic("Assignability cache size", caches.assignable, /*aggregate*/ true); + reportCountStatistic("Identity cache size", caches.identity, /*aggregate*/ true); + reportCountStatistic("Subtype cache size", caches.subtype, /*aggregate*/ true); + reportCountStatistic("Strict subtype cache size", caches.strictSubtype, /*aggregate*/ true); } if (isPerformanceEnabled) { - performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration)); + performance.forEachMeasure((duration, name) => reportTimeStatistic(`${name} time`, duration)); } } else if (isPerformanceEnabled) { @@ -1087,26 +1052,29 @@ namespace ts { if (isPerformanceEnabled) { reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); } - if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) reportAllStatistics(sys, statistics); if (!isPerformanceEnabled) { - sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_or_solutionDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); + sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); } else { performance.disable(); } - return statistics; + } + + function reportStatisticalValue(s: Statistic, aggregate: boolean) { + statistics.push(s); + if (aggregate) buildPerformance.addStatistics(s); } function reportMemoryStatistic(name: string, memoryUsed: number) { - statistics.push({ name, value: memoryUsed, type: StatisticType.memory }); + reportStatisticalValue({ name, value: memoryUsed, type: StatisticType.memory }, /*aggregate*/ true); } - function reportCountStatistic(name: string, count: number) { - statistics.push({ name, value: count, type: StatisticType.count }); + function reportCountStatistic(name: string, count: number, aggregate: boolean) { + reportStatisticalValue({ name, value: count, type: StatisticType.count }, aggregate); } function reportTimeStatistic(name: string, time: number) { - statistics.push({ name, value: time, type: StatisticType.time }); + reportStatisticalValue({ name, value: time, type: StatisticType.time }, /*aggregate*/ true); } } From e6f821926c90f7815a0c237ba0448fd64c41f15f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Jun 2022 11:45:38 -0700 Subject: [PATCH 06/10] More cleanup --- src/compiler/tsbuildPublic.ts | 16 ++++------------ src/executeCommandLine/executeCommandLine.ts | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 5af90aafc5253..b5f6db219c4f3 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1184,14 +1184,6 @@ namespace ts { state: SolutionBuilderState, buildOrder: AnyBuildOrder, reportQueue: boolean - ): InvalidateProjectCreateInfo | undefined { - return getNextInvalidatedProjectCreateInfoWorker(state, buildOrder, reportQueue); - } - - function getNextInvalidatedProjectCreateInfoWorker( - state: SolutionBuilderState, - buildOrder: AnyBuildOrder, - reportQueue: boolean ): InvalidateProjectCreateInfo | undefined { if (!state.projectPendingBuild.size) return undefined; if (isCircularBuildOrder(buildOrder)) return undefined; @@ -1337,11 +1329,11 @@ namespace ts { } } - function getOldProgram(state: SolutionBuilderState, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) { - if (state.options.force) return undefined; - const value = state.builderPrograms.get(proj); + function getOldProgram({ options, builderPrograms, compilerHost }: SolutionBuilderState, proj: ResolvedConfigFilePath, parsed: ParsedCommandLine) { + if (options.force) return undefined; + const value = builderPrograms.get(proj); if (value) return value; - return readBuilderProgram(parsed.options, state.compilerHost) as any as T; + return readBuilderProgram(parsed.options, compilerHost) as any as T; } function afterProgramDone( diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 7b42459a78495..1823200d1fe85 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -1052,6 +1052,7 @@ namespace ts { if (isPerformanceEnabled) { reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); } + reportAllStatistics(sys, statistics); if (!isPerformanceEnabled) { sys.write(Diagnostics.Performance_timings_for_diagnostics_or_extendedDiagnostics_are_not_available_in_this_session_A_native_implementation_of_the_Web_Performance_API_could_not_be_found.message + "\n"); } @@ -1105,7 +1106,6 @@ namespace ts { return (s.value / 1000).toFixed(2) + "s"; case StatisticType.memory: return Math.round(s.value / 1000) + "K"; - break; default: Debug.assertNever(s.type); } From 4daeb874b62e140d0792993ec1031444ced8b3f7 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Jun 2022 12:08:20 -0700 Subject: [PATCH 07/10] Add back build time during watch incremental build --- src/compiler/tsbuildPublic.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index b5f6db219c4f3..66a9abb93f9d3 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1841,6 +1841,14 @@ namespace ts { } function buildNextInvalidatedProject(state: SolutionBuilderState, changeDetected: boolean) { + buildPerformance.mark("beforeBuild"); + const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); + buildPerformance.mark("afterBuild"); + buildPerformance.measure("Build", "beforeBuild", "afterBuild"); + if (buildOrder) reportErrorSummary(state, buildOrder); + } + + function buildNextInvalidatedProjectWorker(state: SolutionBuilderState, changeDetected: boolean) { state.timerToBuildInvalidatedProject = undefined; if (state.reportFileChangeDetected) { state.reportFileChangeDetected = false; @@ -1870,7 +1878,7 @@ namespace ts { } } disableCache(state); - reportErrorSummary(state, buildOrder); + return buildOrder; } function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) { From fa213c8583bee58dba1b5416c6f6c34bdea1ba24 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Jun 2022 13:55:40 -0700 Subject: [PATCH 08/10] More cleanup --- src/executeCommandLine/executeCommandLine.ts | 72 ++++++++------------ 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 1823200d1fe85..0eba7bf36026f 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -874,23 +874,20 @@ namespace ts { cb: ExecuteCommandLineCallbacks, buildHost: SolutionBuilderHostBase ) { - updateCreateProgram(sys, buildHost); + updateCreateProgram(sys, buildHost, /*isBuildMode*/ true); buildHost.afterProgramEmitAndDiagnostics = program => { reportStatistics(sys, program.getProgram()); cb(program); }; - buildHost.afterEmitBundle = config => { - reportStatistics(sys, config); - cb(config); - }; + buildHost.afterEmitBundle = cb; } - function updateCreateProgram(sys: System, host: { createProgram: CreateProgram; }) { + function updateCreateProgram(sys: System, host: { createProgram: CreateProgram; }, isBuildMode: boolean) { const compileUsingBuilder = host.createProgram; host.createProgram = (rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) => { Debug.assert(rootNames !== undefined || (options === undefined && !!oldProgram)); if (options !== undefined) { - enableStatisticsAndTracing(sys, options, /*isBuildMode*/ true); + enableStatisticsAndTracing(sys, options, isBuildMode); } return compileUsingBuilder(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences); }; @@ -901,7 +898,7 @@ namespace ts { cb: ExecuteCommandLineCallbacks, watchCompilerHost: WatchCompilerHost, ) { - updateCreateProgram(sys, watchCompilerHost); + updateCreateProgram(sys, watchCompilerHost, /*isBuildMode*/ false); const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate!; // TODO: GH#18217 watchCompilerHost.afterProgramCreate = builderProgram => { emitFilesUsingBuilder(builderProgram); @@ -976,13 +973,8 @@ namespace ts { } } - function isProgram(programOrConfig: Program | ParsedCommandLine): programOrConfig is Program { - return !!(programOrConfig as Program).getCompilerOptions; - } - function reportStatistics(sys: System, programOrConfig: Program | ParsedCommandLine) { - const program = isProgram(programOrConfig) ? programOrConfig : undefined; - const config = !isProgram(programOrConfig) ? programOrConfig : undefined; - const compilerOptions = program?.getCompilerOptions() || config?.options!; + function reportStatistics(sys: System, program: Program) { + const compilerOptions = program.getCompilerOptions(); if (canTrace(sys, compilerOptions)) { tracing?.stopTracing(); @@ -992,30 +984,28 @@ namespace ts { if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; - if (program) { - reportCountStatistic("Files", program.getSourceFiles().length, /*aggregate*/ true); - - const lineCounts = countLines(program); - const nodeCounts = countNodes(program); - if (compilerOptions.extendedDiagnostics) { - for (const key of arrayFrom(lineCounts.keys())) { - reportCountStatistic("Lines of " + key, lineCounts.get(key)!, /*aggregate*/ true); - } - for (const key of arrayFrom(nodeCounts.keys())) { - reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!, /*aggregate*/ false); - } + reportCountStatistic("Files", program.getSourceFiles().length, /*aggregate*/ true); + + const lineCounts = countLines(program); + const nodeCounts = countNodes(program); + if (compilerOptions.extendedDiagnostics) { + for (const key of arrayFrom(lineCounts.keys())) { + reportCountStatistic("Lines of " + key, lineCounts.get(key)!, /*aggregate*/ true); } - else { - reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ true); - reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ false); + for (const key of arrayFrom(nodeCounts.keys())) { + reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!, /*aggregate*/ false); } - - reportCountStatistic("Identifiers", program.getIdentifierCount(), /*aggregate*/ true); - reportCountStatistic("Symbols", program.getSymbolCount(), /*aggregate*/ true); - reportCountStatistic("Types", program.getTypeCount(), /*aggregate*/ true); - reportCountStatistic("Instantiations", program.getInstantiationCount(), /*aggregate*/ true); + } + else { + reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ true); + reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ false); } + reportCountStatistic("Identifiers", program.getIdentifierCount(), /*aggregate*/ true); + reportCountStatistic("Symbols", program.getSymbolCount(), /*aggregate*/ true); + reportCountStatistic("Types", program.getTypeCount(), /*aggregate*/ true); + reportCountStatistic("Instantiations", program.getInstantiationCount(), /*aggregate*/ true); + if (memoryUsed >= 0) { reportMemoryStatistic("Memory used", memoryUsed); } @@ -1026,13 +1016,11 @@ namespace ts { const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0; const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; if (compilerOptions.extendedDiagnostics) { - if (program) { - const caches = program.getRelationCacheSizes(); - reportCountStatistic("Assignability cache size", caches.assignable, /*aggregate*/ true); - reportCountStatistic("Identity cache size", caches.identity, /*aggregate*/ true); - reportCountStatistic("Subtype cache size", caches.subtype, /*aggregate*/ true); - reportCountStatistic("Strict subtype cache size", caches.strictSubtype, /*aggregate*/ true); - } + const caches = program.getRelationCacheSizes(); + reportCountStatistic("Assignability cache size", caches.assignable, /*aggregate*/ true); + reportCountStatistic("Identity cache size", caches.identity, /*aggregate*/ true); + reportCountStatistic("Subtype cache size", caches.subtype, /*aggregate*/ true); + reportCountStatistic("Strict subtype cache size", caches.strictSubtype, /*aggregate*/ true); if (isPerformanceEnabled) { performance.forEachMeasure((duration, name) => reportTimeStatistic(`${name} time`, duration)); } From 53216fee3910578c2a0e48a745faa5257f173461 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Jun 2022 16:23:17 -0700 Subject: [PATCH 09/10] Feedback --- src/compiler/tsbuildPublic.ts | 16 +++--- src/executeCommandLine/executeCommandLine.ts | 60 ++++++++------------ 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 66a9abb93f9d3..5ae64adc0efe3 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -393,7 +393,7 @@ namespace ts { } configFileCache.set(configFilePath, parsed || diagnostic!); buildPerformance.mark("afterConfigFileParsing"); - buildPerformance.measure("Config File Parsing", "beforeConfigFileParsing", "afterConfigFileParsing"); + buildPerformance.measure("Config file parsing", "beforeConfigFileParsing", "afterConfigFileParsing"); return parsed; } @@ -710,7 +710,7 @@ namespace ts { if (updateOutputFileStampsPending) { updateOutputTimestamps(state, config, projectPath); } - buildPerformance.mark("Projects With Only Timestamp Updates"); + buildPerformance.mark("Timestamps only updates"); return doneInvalidatedProject(state, projectPath); } }; @@ -824,8 +824,8 @@ namespace ts { function done(cancellationToken?: CancellationToken, writeFile?: WriteFileCallback, customTransformers?: CustomTransformers) { executeSteps(BuildStep.Done, cancellationToken, writeFile, customTransformers); - if (kind === InvalidatedProjectKind.Build) buildPerformance.mark("Projects Built"); - else buildPerformance.mark("Bundles Updated"); + if (kind === InvalidatedProjectKind.Build) buildPerformance.mark("Projects built"); + else buildPerformance.mark("Bundles updated"); return doneInvalidatedProject(state, projectPath); } @@ -1723,7 +1723,7 @@ namespace ts { buildPerformance.mark("beforeBuild"); const result = buildWorker(state, project, cancellationToken, writeFile, getCustomTransformers, onlyReferences); buildPerformance.mark("afterBuild"); - buildPerformance.measure("Build", "beforeBuild", "afterBuild"); + buildPerformance.measure("Total", "beforeBuild", "afterBuild"); return result; } @@ -1760,7 +1760,7 @@ namespace ts { buildPerformance.mark("beforeClean"); const result = cleanWorker(state, project, onlyReferences); buildPerformance.mark("afterClean"); - buildPerformance.measure("Clean", "beforeClean", "afterClean"); + buildPerformance.measure("Total", "beforeClean", "afterClean"); return result; } @@ -1844,7 +1844,7 @@ namespace ts { buildPerformance.mark("beforeBuild"); const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); buildPerformance.mark("afterBuild"); - buildPerformance.measure("Build", "beforeBuild", "afterBuild"); + buildPerformance.measure("Total", "beforeBuild", "afterBuild"); if (buildOrder) reportErrorSummary(state, buildOrder); } @@ -2004,7 +2004,7 @@ namespace ts { } } buildPerformance.mark("afterWatcherCreation"); - buildPerformance.measure("Watcher Creation", "beforeWatcherCreation", "afterWatcherCreation"); + buildPerformance.measure("Watcher creation", "beforeWatcherCreation", "afterWatcherCreation"); } function stopWatching(state: SolutionBuilderState) { diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 0eba7bf36026f..5600078e3b07a 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -9,15 +9,6 @@ namespace ts { return counts; } - function countNodes(program: Program): Map { - const counts = getCountsMap(); - forEach(program.getSourceFiles(), file => { - const key = getCountKey(program, file); - counts.set(key, counts.get(key)! + file.nodeCount); - }); - return counts; - } - function getCountsMap() { const counts = new Map(); counts.set("Library", 0); @@ -984,27 +975,22 @@ namespace ts { if (canReportDiagnostics(sys, compilerOptions)) { statistics = []; const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1; - reportCountStatistic("Files", program.getSourceFiles().length, /*aggregate*/ true); + reportCountStatistic("Files", program.getSourceFiles().length); const lineCounts = countLines(program); - const nodeCounts = countNodes(program); if (compilerOptions.extendedDiagnostics) { for (const key of arrayFrom(lineCounts.keys())) { - reportCountStatistic("Lines of " + key, lineCounts.get(key)!, /*aggregate*/ true); - } - for (const key of arrayFrom(nodeCounts.keys())) { - reportCountStatistic("Nodes of " + key, nodeCounts.get(key)!, /*aggregate*/ false); + reportCountStatistic("Lines of " + key, lineCounts.get(key)!); } } else { - reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ true); - reportCountStatistic("Nodes", reduceLeftIterator(nodeCounts.values(), (sum, count) => sum + count, 0), /*aggregate*/ false); + reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0)); } - reportCountStatistic("Identifiers", program.getIdentifierCount(), /*aggregate*/ true); - reportCountStatistic("Symbols", program.getSymbolCount(), /*aggregate*/ true); - reportCountStatistic("Types", program.getTypeCount(), /*aggregate*/ true); - reportCountStatistic("Instantiations", program.getInstantiationCount(), /*aggregate*/ true); + reportCountStatistic("Identifiers", program.getIdentifierCount()); + reportCountStatistic("Symbols", program.getSymbolCount()); + reportCountStatistic("Types", program.getTypeCount()); + reportCountStatistic("Instantiations", program.getInstantiationCount()); if (memoryUsed >= 0) { reportMemoryStatistic("Memory used", memoryUsed); @@ -1017,12 +1003,12 @@ namespace ts { const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0; if (compilerOptions.extendedDiagnostics) { const caches = program.getRelationCacheSizes(); - reportCountStatistic("Assignability cache size", caches.assignable, /*aggregate*/ true); - reportCountStatistic("Identity cache size", caches.identity, /*aggregate*/ true); - reportCountStatistic("Subtype cache size", caches.subtype, /*aggregate*/ true); - reportCountStatistic("Strict subtype cache size", caches.strictSubtype, /*aggregate*/ true); + reportCountStatistic("Assignability cache size", caches.assignable); + reportCountStatistic("Identity cache size", caches.identity); + reportCountStatistic("Subtype cache size", caches.subtype); + reportCountStatistic("Strict subtype cache size", caches.strictSubtype); if (isPerformanceEnabled) { - performance.forEachMeasure((duration, name) => reportTimeStatistic(`${name} time`, duration)); + performance.forEachMeasure((duration, name) => reportTimeStatistic(`${name} time`, duration, /*aggregate*/ true)); } } else if (isPerformanceEnabled) { @@ -1030,15 +1016,15 @@ namespace ts { // 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("I/O read", performance.getDuration("I/O Read"), /*aggregate*/ true); + reportTimeStatistic("I/O write", performance.getDuration("I/O Write"), /*aggregate*/ true); + reportTimeStatistic("Parse time", programTime, /*aggregate*/ true); + reportTimeStatistic("Bind time", bindTime, /*aggregate*/ true); + reportTimeStatistic("Check time", checkTime, /*aggregate*/ true); + reportTimeStatistic("Emit time", emitTime, /*aggregate*/ true); } if (isPerformanceEnabled) { - reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime); + reportTimeStatistic("Total time", programTime + bindTime + checkTime + emitTime, /*aggregate*/ false); } reportAllStatistics(sys, statistics); if (!isPerformanceEnabled) { @@ -1058,12 +1044,12 @@ namespace ts { reportStatisticalValue({ name, value: memoryUsed, type: StatisticType.memory }, /*aggregate*/ true); } - function reportCountStatistic(name: string, count: number, aggregate: boolean) { - reportStatisticalValue({ name, value: count, type: StatisticType.count }, aggregate); + function reportCountStatistic(name: string, count: number) { + reportStatisticalValue({ name, value: count, type: StatisticType.count }, /*aggregate*/ true); } - function reportTimeStatistic(name: string, time: number) { - reportStatisticalValue({ name, value: time, type: StatisticType.time }, /*aggregate*/ true); + function reportTimeStatistic(name: string, time: number, aggregate: boolean) { + reportStatisticalValue({ name, value: time, type: StatisticType.time }, aggregate); } } From cdf2962236406fe0a6e857f8b6be72b0315519c8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 8 Jun 2022 16:57:06 -0700 Subject: [PATCH 10/10] Some changes per feedback --- src/compiler/tsbuildPublic.ts | 6 +++--- src/executeCommandLine/executeCommandLine.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index ebbeaa990ed3d..d07998d48ef9f 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1955,7 +1955,7 @@ namespace ts { buildPerformance.mark("beforeBuild"); const result = buildWorker(state, project, cancellationToken, writeFile, getCustomTransformers, onlyReferences); buildPerformance.mark("afterBuild"); - buildPerformance.measure("Total", "beforeBuild", "afterBuild"); + buildPerformance.measure("Build", "beforeBuild", "afterBuild"); return result; } @@ -1992,7 +1992,7 @@ namespace ts { buildPerformance.mark("beforeClean"); const result = cleanWorker(state, project, onlyReferences); buildPerformance.mark("afterClean"); - buildPerformance.measure("Total", "beforeClean", "afterClean"); + buildPerformance.measure("Clean", "beforeClean", "afterClean"); return result; } @@ -2076,7 +2076,7 @@ namespace ts { buildPerformance.mark("beforeBuild"); const buildOrder = buildNextInvalidatedProjectWorker(state, changeDetected); buildPerformance.mark("afterBuild"); - buildPerformance.measure("Total", "beforeBuild", "afterBuild"); + buildPerformance.measure("Build", "beforeBuild", "afterBuild"); if (buildOrder) reportErrorSummary(state, buildOrder); } diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 5600078e3b07a..1034ebe0bfa98 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -1037,7 +1037,7 @@ namespace ts { function reportStatisticalValue(s: Statistic, aggregate: boolean) { statistics.push(s); - if (aggregate) buildPerformance.addStatistics(s); + if (aggregate) buildPerformance.addStatistics({ ...s, name: `Aggregate ${s.name}` }); } function reportMemoryStatistic(name: string, memoryUsed: number) {