Skip to content

Commit 48fec08

Browse files
committed
perf(compiler-cli): refactor the performance tracing infrastructure (angular#41125)
ngtsc has an internal performance tracing package, which previously has not really seen much use. It used to track performance statistics on a very granular basis (microseconds per actual class analysis, for example). This had two problems: * it produced voluminous amounts of data, complicating the analysis of such results and providing dubious value. * it added nontrivial overhead to compilation when used (which also affected the very performance of the operations being measured). This commit replaces the old system with a streamlined performance tracing setup which is lightweight and designed to be always-on. The new system tracks 3 metrics: * time taken by various phases and operations within the compiler * events (counters) which measure the shape and size of the compilation * memory usage measured at various points of the compilation process If the compiler option `tracePerformance` is set, the compiler will serialize these metrics to a JSON file at that location after compilation is complete. PR Close angular#41125
1 parent dd82c8e commit 48fec08

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1183
-633
lines changed

packages/bazel/src/ngc-wrapped/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ ts_library(
1616
],
1717
deps = [
1818
"//packages/compiler-cli",
19+
"//packages/compiler-cli/src/ngtsc/perf",
1920
"@npm//@bazel/typescript",
2021
"@npm//@types/node",
2122
"@npm//tsickle",

packages/bazel/src/ngc-wrapped/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import * as ng from '@angular/compiler-cli';
10+
import {PerfPhase} from '@angular/compiler-cli/src/ngtsc/perf';
1011
import {BazelOptions, CachedFileLoader, CompilerHost, constructManifest, debug, FileCache, FileLoader, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop, UncachedFileLoader} from '@bazel/typescript';
1112
import * as fs from 'fs';
1213
import * as path from 'path';
@@ -515,6 +516,12 @@ function gatherDiagnosticsForInputsOnly(
515516
options: ng.CompilerOptions, bazelOpts: BazelOptions,
516517
ngProgram: ng.Program): (ng.Diagnostic|ts.Diagnostic)[] {
517518
const tsProgram = ngProgram.getTsProgram();
519+
520+
// For the Ivy compiler, track the amount of time spent fetching TypeScript diagnostics.
521+
let previousPhase = PerfPhase.Unaccounted;
522+
if (ngProgram instanceof ng.NgtscProgram) {
523+
previousPhase = ngProgram.compiler.perfRecorder.phase(PerfPhase.TypeScriptDiagnostics);
524+
}
518525
const diagnostics: (ng.Diagnostic|ts.Diagnostic)[] = [];
519526
// These checks mirror ts.getPreEmitDiagnostics, with the important
520527
// exception of avoiding b/30708240, which is that if you call
@@ -529,6 +536,11 @@ function gatherDiagnosticsForInputsOnly(
529536
diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf));
530537
diagnostics.push(...tsProgram.getSemanticDiagnostics(sf));
531538
}
539+
540+
if (ngProgram instanceof ng.NgtscProgram) {
541+
ngProgram.compiler.perfRecorder.phase(previousPhase);
542+
}
543+
532544
if (!diagnostics.length) {
533545
// only gather the angular diagnostics if we have no diagnostics
534546
// in any other files.

packages/compiler-cli/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ export {CompilerOptions as AngularCompilerOptions} from './src/transformers/api'
2222

2323
export {ngToTsDiagnostic} from './src/transformers/util';
2424
export {NgTscPlugin} from './src/ngtsc/tsc_plugin';
25+
export {NgtscProgram} from './src/ngtsc/program';
2526

2627
setFileSystem(new NodeJSFileSystem());

packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {ConstantPool} from '@angular/compiler';
9+
import {NOOP_PERF_RECORDER} from '@angular/compiler-cli/src/ngtsc/perf';
910
import * as ts from 'typescript';
1011

1112
import {ParsedConfiguration} from '../../..';
@@ -89,7 +90,7 @@ export class DecorationAnalyzer {
8990
fullRegistry = new CompoundMetadataRegistry([this.metaRegistry, this.scopeRegistry]);
9091
evaluator =
9192
new PartialEvaluator(this.reflectionHost, this.typeChecker, /* dependencyTracker */ null);
92-
importGraph = new ImportGraph(this.typeChecker);
93+
importGraph = new ImportGraph(this.typeChecker, NOOP_PERF_RECORDER);
9394
cycleAnalyzer = new CycleAnalyzer(this.importGraph);
9495
injectableRegistry = new InjectableClassRegistry(this.reflectionHost);
9596
typeCheckScopeRegistry = new TypeCheckScopeRegistry(this.scopeRegistry, this.fullMetaReader);
@@ -104,7 +105,8 @@ export class DecorationAnalyzer {
104105
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,
105106
CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER,
106107
NOOP_DEPENDENCY_TRACKER, this.injectableRegistry,
107-
/* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler),
108+
/* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler,
109+
NOOP_PERF_RECORDER),
108110

109111
// See the note in ngtsc about why this cast is needed.
110112
// clang-format off
@@ -117,23 +119,26 @@ export class DecorationAnalyzer {
117119
// version 10, undecorated classes that use Angular features are no longer handled
118120
// in ngtsc, but we want to ensure compatibility in ngcc for outdated libraries that
119121
// have not migrated to explicit decorators. See: https://hackmd.io/@alx/ryfYYuvzH.
120-
/* compileUndecoratedClassesWithAngularFeatures */ true
122+
/* compileUndecoratedClassesWithAngularFeatures */ true,
123+
NOOP_PERF_RECORDER
121124
) as DecoratorHandler<unknown, unknown, SemanticSymbol|null,unknown>,
122125
// clang-format on
123126
// Pipe handler must be before injectable handler in list so pipe factories are printed
124127
// before injectable factories (so injectable factories can delegate to them)
125128
new PipeDecoratorHandler(
126129
this.reflectionHost, this.evaluator, this.metaRegistry, this.scopeRegistry,
127-
NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore),
130+
NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore, NOOP_PERF_RECORDER),
128131
new InjectableDecoratorHandler(
129132
this.reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, this.isCore,
130-
/* strictCtorDeps */ false, this.injectableRegistry, /* errorOnDuplicateProv */ false),
133+
/* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER,
134+
/* errorOnDuplicateProv */ false),
131135
new NgModuleDecoratorHandler(
132136
this.reflectionHost, this.evaluator, this.fullMetaReader, this.fullRegistry,
133137
this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null,
134138
this.refEmitter,
135139
/* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER,
136-
!!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry),
140+
!!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry,
141+
NOOP_PERF_RECORDER),
137142
];
138143
compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost);
139144
migrations: Migration[] = [

packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ts_library(
1818
"//packages/compiler-cli/src/ngtsc/indexer",
1919
"//packages/compiler-cli/src/ngtsc/metadata",
2020
"//packages/compiler-cli/src/ngtsc/partial_evaluator",
21+
"//packages/compiler-cli/src/ngtsc/perf",
2122
"//packages/compiler-cli/src/ngtsc/reflection",
2223
"//packages/compiler-cli/src/ngtsc/routing",
2324
"//packages/compiler-cli/src/ngtsc/scope",

packages/compiler-cli/src/ngtsc/annotations/src/component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {extractSemanticTypeParameters, isArrayEqual, isReferenceEqual, SemanticD
1818
import {IndexingContext} from '../../indexer';
1919
import {ClassPropertyMapping, ComponentResources, DirectiveMeta, DirectiveTypeCheckMeta, extractDirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, Resource, ResourceRegistry} from '../../metadata';
2020
import {EnumValue, PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
21+
import {PerfEvent, PerfRecorder} from '../../perf';
2122
import {ClassDeclaration, DeclarationNode, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
2223
import {ComponentScopeReader, LocalModuleScopeRegistry, TypeCheckScopeRegistry} from '../../scope';
2324
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
@@ -208,7 +209,7 @@ export class ComponentDecoratorHandler implements
208209
private depTracker: DependencyTracker|null,
209210
private injectableRegistry: InjectableClassRegistry,
210211
private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
211-
private annotateForClosureCompiler: boolean) {}
212+
private annotateForClosureCompiler: boolean, private perf: PerfRecorder) {}
212213

213214
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
214215
private elementSchemaRegistry = new DomElementSchemaRegistry();
@@ -309,6 +310,7 @@ export class ComponentDecoratorHandler implements
309310
analyze(
310311
node: ClassDeclaration, decorator: Readonly<Decorator>,
311312
flags: HandlerFlags = HandlerFlags.NONE): AnalysisOutput<ComponentAnalysisData> {
313+
this.perf.eventCount(PerfEvent.AnalyzeComponent);
312314
const containingFile = node.getSourceFile().fileName;
313315
this.literalCache.delete(decorator);
314316

packages/compiler-cli/src/ngtsc/annotations/src/directive.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {areTypeParametersEqual, extractSemanticTypeParameters, isArrayEqual, isS
1616
import {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateGuardMeta} from '../../metadata';
1717
import {extractDirectiveTypeCheckMeta} from '../../metadata/src/util';
1818
import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator';
19+
import {PerfEvent, PerfRecorder} from '../../perf';
1920
import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
2021
import {LocalModuleScopeRegistry} from '../../scope';
2122
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
@@ -180,7 +181,7 @@ export class DirectiveDecoratorHandler implements
180181
private injectableRegistry: InjectableClassRegistry, private isCore: boolean,
181182
private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
182183
private annotateForClosureCompiler: boolean,
183-
private compileUndecoratedClassesWithAngularFeatures: boolean) {}
184+
private compileUndecoratedClassesWithAngularFeatures: boolean, private perf: PerfRecorder) {}
184185

185186
readonly precedence = HandlerPrecedence.PRIMARY;
186187
readonly name = DirectiveDecoratorHandler.name;
@@ -211,6 +212,8 @@ export class DirectiveDecoratorHandler implements
211212
return {diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)]};
212213
}
213214

215+
this.perf.eventCount(PerfEvent.AnalyzeDirective);
216+
214217
const directiveResult = extractDirectiveMetadata(
215218
node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore,
216219
flags, this.annotateForClosureCompiler);

packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as ts from 'typescript';
1212
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
1313
import {DefaultImportRecorder} from '../../imports';
1414
import {InjectableClassRegistry} from '../../metadata';
15+
import {PerfEvent, PerfRecorder} from '../../perf';
1516
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
1617
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../transform';
1718

@@ -34,7 +35,7 @@ export class InjectableDecoratorHandler implements
3435
constructor(
3536
private reflector: ReflectionHost, private defaultImportRecorder: DefaultImportRecorder,
3637
private isCore: boolean, private strictCtorDeps: boolean,
37-
private injectableRegistry: InjectableClassRegistry,
38+
private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder,
3839
/**
3940
* What to do if the injectable already contains a ɵprov property.
4041
*
@@ -64,6 +65,8 @@ export class InjectableDecoratorHandler implements
6465

6566
analyze(node: ClassDeclaration, decorator: Readonly<Decorator>):
6667
AnalysisOutput<InjectableHandlerData> {
68+
this.perf.eventCount(PerfEvent.AnalyzeInjectable);
69+
6770
const meta = extractInjectableMetadata(node, decorator, this.reflector);
6871
const decorators = this.reflector.getDecoratorsOfDeclaration(node);
6972

packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports'
1414
import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph';
1515
import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
1616
import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
17+
import {PerfEvent, PerfRecorder} from '../../perf';
1718
import {ClassDeclaration, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection';
1819
import {NgModuleRouteAnalyzer} from '../../routing';
1920
import {LocalModuleScopeRegistry, ScopeData} from '../../scope';
@@ -131,7 +132,8 @@ export class NgModuleDecoratorHandler implements
131132
private factoryTracker: FactoryTracker|null,
132133
private defaultImportRecorder: DefaultImportRecorder,
133134
private annotateForClosureCompiler: boolean,
134-
private injectableRegistry: InjectableClassRegistry, private localeId?: string) {}
135+
private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder,
136+
private localeId?: string) {}
135137

136138
readonly precedence = HandlerPrecedence.PRIMARY;
137139
readonly name = NgModuleDecoratorHandler.name;
@@ -154,6 +156,8 @@ export class NgModuleDecoratorHandler implements
154156

155157
analyze(node: ClassDeclaration, decorator: Readonly<Decorator>):
156158
AnalysisOutput<NgModuleAnalysis> {
159+
this.perf.eventCount(PerfEvent.AnalyzeNgModule);
160+
157161
const name = node.name.text;
158162
if (decorator.args === null || decorator.args.length > 1) {
159163
throw new FatalDiagnosticError(

packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {DefaultImportRecorder, Reference} from '../../imports';
1414
import {SemanticSymbol} from '../../incremental/semantic_graph';
1515
import {InjectableClassRegistry, MetadataRegistry} from '../../metadata';
1616
import {PartialEvaluator} from '../../partial_evaluator';
17+
import {PerfEvent, PerfRecorder} from '../../perf';
1718
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
1819
import {LocalModuleScopeRegistry} from '../../scope';
1920
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../transform';
@@ -55,7 +56,8 @@ export class PipeDecoratorHandler implements
5556
private reflector: ReflectionHost, private evaluator: PartialEvaluator,
5657
private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry,
5758
private defaultImportRecorder: DefaultImportRecorder,
58-
private injectableRegistry: InjectableClassRegistry, private isCore: boolean) {}
59+
private injectableRegistry: InjectableClassRegistry, private isCore: boolean,
60+
private perf: PerfRecorder) {}
5961

6062
readonly precedence = HandlerPrecedence.PRIMARY;
6163
readonly name = PipeDecoratorHandler.name;
@@ -78,6 +80,8 @@ export class PipeDecoratorHandler implements
7880

7981
analyze(clazz: ClassDeclaration, decorator: Readonly<Decorator>):
8082
AnalysisOutput<PipeHandlerData> {
83+
this.perf.eventCount(PerfEvent.AnalyzePipe);
84+
8185
const name = clazz.name.text;
8286
const type = wrapTypeReference(this.reflector, clazz);
8387
const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));

0 commit comments

Comments
 (0)