New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf: AOT compilation optimizations #38539
Conversation
packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts
Outdated
Show resolved
Hide resolved
75e321e
to
f740b04
Compare
// BoundTarget, which is similar to a ts.TypeChecker. | ||
const binder = new R3TargetBinder(matcher); | ||
const bound = binder.bind({template: metadata.template.nodes}); | ||
|
||
// The BoundTarget knows which directives and pipes matched the template. | ||
const usedDirectives = bound.getUsedDirectives(); | ||
const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!); | ||
const usedDirectives = bound.getUsedDirectives().map(directive => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if instead, we make expression
inside the directive metadata into a closure which produces the expression only on demand? That would fix the performance issues and simplify all the downstream logic, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually quite like the way it currently is. The result of the closure would ideally be memoized to avoid repeated computations. This is certainly something we could do; I just think I prefer this straightforward post-processing of the matched directives. Pipes need to be processed in the same way anyway, so currently it's nicely symmetrical. Additionally, the SelectorMatcher
is currently context-free so could theoretically also be cached (but the benefits of that are quite small).
If you feel like we should continue capturing all directives in the resolve metadata (for subsequent compilation step to use) then it has to become a closure. Currently, storing only the reduced set is actually beneficial to perf downstream as there will be less directives to match in TemplateDefinitionBuilder
(actually that becomes a noop as all directives will be matched) and the linker work also benefits from not having to do directive matching itself.
f740b04
to
59afd0f
Compare
8d4feef
to
31c3d62
Compare
merge-assistance: presubmit was green; this was rebased to resolve a conflict in an import declaration. |
…ion (#38539) When type-checking a component, the declaring NgModule scope is used to create a directive matcher that contains flattened directive metadata, i.e. the metadata of a directive and its base classes. This computation is done for all components, whereas the type-check scope is constant per NgModule. Additionally, the flattening of metadata is constant per directive instance so doesn't necessarily have to be recomputed for each component. This commit introduces a `TypeCheckScopes` class that is responsible for flattening directives and computing the scope per NgModule. It caches the computed results as appropriate to avoid repeated computation. PR Close #38539
…information (angular#38539)" This reverts commit ba95b79. internal failure: https://test.corp.google.com/ui#id=OCL:329948619:BASE:329967516:1599160428139:d63165ae
…are used (angular#38539)" This reverts commit 4faac78. internal failure: https://test.corp.google.com/ui#id=OCL:329948619:BASE:329967516:1599160428139:d63165ae
…are used (#38539)" (#38765) This reverts commit 4faac78. internal failure: https://test.corp.google.com/ui#id=OCL:329948619:BASE:329967516:1599160428139:d63165ae PR Close #38765
Recent work on compiler internals in angular#38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in angular#38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks angular#38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`.
Recent work on compiler internals in angular#38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in angular#38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks angular#38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`.
Recent work on compiler internals in angular#38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in angular#38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks angular#38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`.
Blocked on #38810. Once that is in, it's probably a good idea to run a global presubmit for this one so that there's less risk of needing to revert again. |
Recent work on compiler internals in #38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in #38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks #38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`. PR Close #38810
Recent work on compiler internals in #38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in #38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks #38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`. PR Close #38810
31c3d62
to
94840a9
Compare
For the compilation of a component, the compiler has to prepare some information about the directives and pipes that are used in the template. This information includes an expression for directives/pipes, for usage within the compilation output. For large NgModule compilation scopes this has shown to introduce a performance hotspot, as the generation of expressions is quite expensive. This commit reduces the performance overhead by only generating expressions for the directives/pipes that are actually used within the template, significantly cutting down on the compiler's resolve phase.
When type-checking a component, the declaring NgModule scope is used to create a directive matcher that contains flattened directive metadata, i.e. the metadata of a directive and its base classes. This computation is done for all components, whereas the type-check scope is constant per NgModule. Additionally, the flattening of metadata is constant per directive instance so doesn't necessarily have to be recomputed for each component. This commit introduces a `TypeCheckScopes` class that is responsible for flattening directives and computing the scope per NgModule. It caches the computed results as appropriate to avoid repeated computation.
94840a9
to
faeeea1
Compare
Presubmit + Global Presubmit (with Ivy). |
FYI, presubmits (including a global one) went well. |
…ion (#38539) When type-checking a component, the declaring NgModule scope is used to create a directive matcher that contains flattened directive metadata, i.e. the metadata of a directive and its base classes. This computation is done for all components, whereas the type-check scope is constant per NgModule. Additionally, the flattening of metadata is constant per directive instance so doesn't necessarily have to be recomputed for each component. This commit introduces a `TypeCheckScopes` class that is responsible for flattening directives and computing the scope per NgModule. It caches the computed results as appropriate to avoid repeated computation. PR Close #38539
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
See individual commits