angular: Incremental AOT compilation is 5 times slower in Angular 11.1 comparing to 10.1

🐞 bug report

Is this a regression?

Yes, it was better in Angular 10.1.

Description

After upgrading the app to 11.1 I observed regression in build time - both initial and incremental. While intial build time increased by 10% (150s to 165s), further incremental compilations in watch mode looks times slower.

There are two different cases I wanted to describe:

  • Making an edit in a page-level component - it has a number of dependencies on its own, but relatively few other modules depend on it; it’s imported directly by the routing module.
  • Making an edit into a low-level shared module that is used by virtually every component in the app.

Here is the timing breakdown:

Angular 10.1 Angular 11.1
initial 150s 165s
incremental - page-level edit 3s 8s
incremental - shared low-level edit 9s 40s ❗️

CPU profile (via node --inspect) shows something very strange for Angular 11: a better half of the time it spends in analyzingFileEmitter Screen Shot 2021-01-29 at 2 52 37 PM

🌍 Your Environment

Angular Version:


Angular CLI: 11.1.2
Node: 12.14.1
OS: darwin x64

Angular: 11.1.1
... animations, cdk, common, compiler, compiler-cli, core, forms
... language-service, material, platform-browser
... platform-browser-dynamic, platform-server, router
Ivy Workspace: Yes

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1101.2
@angular-devkit/build-angular   0.1101.2
@angular-devkit/core            11.1.2
@angular-devkit/schematics      11.1.2
@angular/cli                    11.1.2
@nguniversal/builders           11.1.1
@nguniversal/express-engine     11.1.1
@schematics/angular             11.1.2
@schematics/update              0.1101.2
rxjs                            6.6.3
typescript                      4.1.3

Anything else relevant?

  • I tried disabling source maps. While it does speed up compilation, it’s still 24s (down from 40s with source maps) for the slow incremental case - way too long.
  • Also "showCircularDependencies": false
Complete `build` section from `angular.json`
"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "outputPath": "dist/browser",
    "index": "src/index.html",
    "main": "src/main.ts",
    "tsConfig": "src/tsconfig.app.json",
    "polyfills": "src/polyfills.ts",
    "preserveSymlinks": true,
    "assets": [
      "src/assets",
      "src/views"
    ],
    "aot": true,
    "progress": false,
    "styles": [
      "src/app/app.scss"
    ],
    "stylePreprocessorOptions": {
      "includePaths": [
        "node_modules"
      ]
    },
    "scripts": [
      {
        "input": "node_modules/browser-update/update.npm.full.js",
        "inject": false,
        "bundleName": "browser-update-3.0"
      },
      {
        "input": "src/browser-update-config.js",
        "inject": false,
        "bundleName": "browser-update-3.0"
      }
    ],
    "namedChunks": true,
    "showCircularDependencies": false,
    "allowedCommonJsDependencies": [
      "lodash",
      "numeral",
      "@splitsoftware/splitio",
      "fuzzysort",
      "hellojs",
      "stale-lru-cache",
      "card-validator",
      "graphql-tag",
      "getstream",
      "@babel/runtime-corejs3",
      "core-js-pure",
      "axios",
      "zen-observable",
      "utfx",
      "jwt-decode"
    ]
  },
  "configurations": {
    "production": {
      "budgets": [
        {
          "type": "anyComponentStyle",
          "maximumWarning": "6kb"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": {
        "scripts": true,
        "styles": false,
        "hidden": true,
        "vendor": false
      },
      "extractCss": true,
      "namedChunks": true,
      "extractLicenses": true,
      "vendorChunk": true,
      "buildOptimizer": true,
      "statsJson": true,
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        },
        {
          "replace": "src/environments/development-modules.ts",
          "with": "src/environments/development-modules.prod.ts"
        },
        {
          "replace": "src/environments/development-keys.ts",
          "with": "src/environments/development-keys.prod.ts"
        },
        {
          "replace": "src/main.ts",
          "with": "src/main.prod.ts"
        }
      ]
    },
    "es5": {
      "tsConfig": "src/tsconfig.es5.json"
    }
  }
},

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 57
  • Comments: 69 (41 by maintainers)

Commits related to this issue

Most upvoted comments

These increased build times have destroyed our productivity with angular. i cant believe its getting worse. if your not google it seems your not really relevant. this is a years long problem of continued increase in compile times.

personally i’m done with angular now, it wasnt for the fact we have a huge investment in it, i’d dump the entire codebase. the cost of producing code with angular is unsustainable for us.

frankly it seems that the angular team is more interested in its own goals, and probably serving googles needs than the community at large.

i guess i was naive to think it would ever be any different.

@Splaktar my attitude was not the best, I know, and please accept my apologies for that.

I know that Angular is a free and Open source project, but many companies based their products on this framework and spending a lot of money, and Is difficult to understand how this kind of performance issues can appear.

In my case, imagine our project with more than 8000 modules, how long it can take to recompile every single change?

Angular is an amazing product, I am a great supporter. Thanks to the angular team and, again, accept my apologies.

Hello all,

with the release of Angular 11.2.5 just now these performance problems should have been resolved; it could be that you’ll even see initial builds being a bit faster.

I have been doing some tests on a large project by changing a component that was used in many other components. With NG_BUILD_IVY_LEGACY=1 this used to take about 10s, whereas NG_BUILD_IVY_LEGACY=0 (or the default) would be ~15s. Now, with 11.2.5, this has been improved to 2-3s. So not only has the regression in 11.1 been resolved, this has been the largest improvement to incremental rebuild times since AOT was enabled by default in v9.

There are some cases where incremental rebuilds can take a bit longer. Changes to directive inputs, outputs, selectors and some other stuff causes all components that use the changed directive to be recompiled. This could mean that changing a directive/component that is used throughout an application can still result in rebuild times as they were (i.e. 15s), but the expectation is that most changes will not be as expensive.

I am super curious what these improvements mean for your applications, so please do update to 11.2.5 and share your experiences here! For anyone still seeing issues, please open new issues for us to investigate.

Are you fully aware how a problem like that can kill your product?

@sergidt also please note that this is free and open source, it’s not a paid product. If a problem is identified, it gets investigated and fixes are proposed. There is no guaranteed SLA on rebuild times or anything else. We’re all here building and working to make things better and faster, together.

As @mlc-mlapis said above, it’s a good idea to test your app and the dev experience with each new Angular version before you push out a commit that moves your whole team to the new version. I just did this in another project and we were able to disable Ivy and AOT in dev mode so that developers could still be productive until this gets resolved. Thankfully these workarounds exist (thanks to the helpful community in this thread for helping share and test them) and we are able to get some of the other benefits included in 11.1 without having to roll back to 11.0.

I don’t understand what kind of tests they pass. Is inapcetable.

You have to fix this problems rather than release a version every 6 months.

I have bet everything for angular in my last project but receiving nothing in return.

@artaommahe The latest CLI containing the perf fixes has been released (11.2.5)

@mickdelaney There’s literally an option to reduce the times back to what they were whilst they try and find a fix. Calm down.

Thanks everyone for the reports. I did some profiling of my own using the ng9-aot-build-times repo. What seems to be happening in comparison to the prior Webpack integration pipeline is that now the files that need to be updated according to the compiler are actually incorporated into to rebuilt bundle. This was done to fix bugs where an incremental rebuild did not result in a fully up-to-date bundle.

Unfortunately however, the Angular compiler is fairly pessimistic in its ability to determine which files need to be updated. I have some ideas to improve this situation but this effort is still underway.

@JoostK last update solved our issues, thanks

angular v11.2.10, angular/cli v11.2.9

NG_BUILD_IVY_LEGACY server start AppComponent shared service lazy module component
0 66s 3s 3.2s 2.8s

@mlc-mlapis this kind of mistakes cannot occur in a framework like angular.

If you release a new version the first think you have to test is the productivity of that tool.

How many developers are using angular?

Are you fully aware how a problem like that can kill your product?

I wish dependabot wouldn’t spam threads like this 😦

@Delagen we have established an issue in the Ivy Webpack pipeline and this has been addressed in angular/angular-cli#20236. A PR to apply that to patch is pending; once that’s been merged I’ll post a link here of the build artefacts before it releases next week. Local testing shows that it does avoid the superfluous emits; we were incorrectly registering transitive dependencies, not direct dependencies. In the repo I tested with this is only about 400ms so it wasn’t a concern earlier, but certain projects may behave differently of course.

@sergidt Hmm, the first thing we do with any, even minor/patch release of everything is that we do our own tests. After successful results, we apply it to development teams. So you should agree with me that this case was also your fault.

Sorry? My fault? Are you joking? what a lack of professionalism is that? Am I an angular core member?

There’s literally an option to reduce the times back to what they were whilst they try and find a fix.

To be fair, that option is hard to find. Angular should just release a patch with reverting to the old plugin by default.

Thanks for reporting. I suspect the difference is due to the CLI’s new Webpack integration, as there haven’t been any relevant changes on the framework side. If you set environment variable NG_BUILD_IVY_LEGACY=1 the CLI will fallback to the previous Webpack integration implementation, perhaps you could try to see if using that would improve the times to what they were before. It could still be that the new CLI integration is limited by how the compiler operates, so I’ll not move this issue to the CLI repo right away.

That said, do you think it would be possible to share a reproduction so that we can investigate further?

Isn’t that what I described in #40635 (comment)? 😃

I like that with this change I can remove the paths workaround again. But @artaommahe said in #40635 (comment) he only has one node_modules directory, so it may not help him.

I think it’s not exactly as you described in https://github.com/angular/angular/issues/40635#issuecomment-802989789, but it might be that #41448 could help addressing that scenario as well.

Consider the following dependencies

A -> E@2.0
B -> E@2.0
C -> E@1.0
D -> E@1.0

Because of the incompatible version constraints, a maximum of one version of E can be hoisted and the others get their own local instance:

node_modules
  A
  B
  C
    node_modules
      E@1.0
  D
    node_modules
      E@1.0
  E@2.0

(it could also have hoisted E@1.0 or not have hoisted E at all, that depends on the package manager and possibly the lock file)

When TypeScript includes C and D into the compilation unit, it notices that the source files for both copies of E@1.0 correspond with the exact same version (as determined by their package.json file based on name and version) which results in a redirect from one of the E@1.0 copies to the other one. This may also happen in the scenario you described with the totally foreign node_modules, so perhaps it does indeed avoid the need for the path mappings.

By the way, the long term plan is to remove the bailout for changed .d.ts files entirely but doing so requires a bit more work and testing effort than the simple change in #41448.

can anyone point out where exactly to set the NG_BUILD_IVY_LEGACY=1 env? Is it in the angular.json file?

@mickdelaney I’m sorry to hear that. In fact, there have been a lot of performance improvements over the past year after the default compilation mode has switched to AOT compilation in v9. It could be that AOT compilation is indeed a lot slower than the JIT compile times you were used to in older versions of Angular; the AOT compilation comes with the overhead of precompiling all components and doing template type checking, both of which is not done when using JIT. You can always go back to JIT compilations during development though: if you’re using the CLI this is controlled by the aot flag in angular.json.

i wasnt talking specifically about the 10x - 11x but rather the fact that 10x itself is horribly slow. our compile times on older angular versions were far better. and we’ve spent so much time trying to optimize it. we’ve looked and spent time using nrwl nx, caching, buildable libraries, all of that. the fact we need to spend so much time on it is crazy. i had hoped that the 11x version would start to improve the build times.

@billdwhite @sergidt Be judicial and remember your last mistake and how surprised you were that it could have happened.

@mlc-mlapis: my apologies if I came off as critical. Not my intention. Just happy that the issue is being worked on 😃

@sergidt Hmm, the first thing we do with any, even minor/patch release of everything is that we do our own tests. After successful results, we apply it to development teams. So you should agree with me that this case was also your fault.

@billdwhite @sergidt Be judicial and remember your last mistake and how surprised you were that it could have happened.

NG_BUILD_IVY_LEGACY=1 ng serve

Same here. Rebuild went up from 3s to 30s for our project in monorepo (10.1.4 -> 11.1.1 update). NG_BUILD_IVY_LEGACY=1 helps, 3s again.

Some data for rebuild time difference between 11.1.1 default and NG_BUILD_IVY_LEGACY=1 modes (average of 4 passes)

project1 3s -> 30s
project2 4.7s -> 95s
project3 10s -> 180s
project4 3.4s -> 20s

Timings for ng serve start time or ng build --prod has not changed between these modes. Source maps are disabled.

packages versions

Angular CLI: 11.1.2
Node: 12.18.1
OS: win32 x64

Angular: 11.1.1
... animations, cdk, common, compiler, compiler-cli, core
... elements, forms, language-service, localize, material
... platform-browser, platform-browser-dynamic, platform-server
... router
Ivy Workspace: Yes

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1101.2
@angular-devkit/build-angular   0.1101.2
@angular-devkit/core            11.1.2
@angular-devkit/schematics      11.0.7
@angular/cli                    11.1.2
@angular/flex-layout            11.0.0-beta.33
@ngtools/webpack                11.1.2
@nguniversal/builders           11.1.1
@nguniversal/express-engine     10.1.0
@schematics/angular             11.1.2
@schematics/update              0.1101.2
rxjs                            6.6.2
typescript                      4.1.3
webpack                         4.46.0

In our project upgrading from 11.1 to 11.2 didn’t show any performance difference with NG_BUILD_IVY_LEGACY. More than that, disabling NG_BUILD_IVY_LEGACY didn’t have any performance hits.

So with 11.2 we were able to get rid of NG_BUILD_IVY_LEGACY in our build scripts without any drawback.

Hi, I know this thread is officially closed, but I am hoping someone who was part of this discussion and managed to solve it could point me to the right direction.

We just upgraded to v11 and also experience significant issues with our built times.

Current state:

Angular CLI: 11.2.11 Node: 14.16.0 OS: darwin x64

Angular: 11.2.12 … animations, common, compiler, compiler-cli, core, forms … localize, platform-browser, platform-browser-dynamic … platform-server, router, service-worker Ivy Workspace: Yes

@angular-devkit/build-angular@0.1102.11 │ └── webpack@4.44.2

Package Version
@angular-devkit/architect 0.1102.11
@angular-devkit/build-angular 0.1102.11
@angular-devkit/core 11.2.11
@angular-devkit/schematics 11.2.11
@angular/cdk 11.2.11
@angular/cli 11.2.11
@angular/material 11.2.11
@schematics/angular 11.2.11
@schematics/update 0.1102.11
rxjs 6.6.7
typescript 4.0.7
NG_BUILD_IVY_LEGACY server start AppComponent lazy module component
0 223.107s 163.682s 148.481s
1 274.018s 138.859s 111.629s

When we were on version 10

server start lazy module component
147.197s 53.614s

Any ideas are welcome and thank you in advance!

With some additional information from @artaommahe we’ve established a scenario where the compiler bailed out of incremental reuse. It would happen when a package is present in multiple physical locations on disk (e.g. when a package hasn’t been hoisted to the root node_modules but appears in multiple local node_modules directories), which are handled by TypeScript in a special way. This is being addressed in #41448.

@JoostK

angular v11.2.6, angular/cli v11.2.5 don’t see a lot of change 😕 (~1s rebuild time drop relatively to previous patch version)

NG_BUILD_IVY_LEGACY server start AppComponent shared service lazy module component
1 67s 2.7s 2.5s 2.6s
0 63s 15.5s 14.3s 15s

am i still missing smth?)

CPU-20210319T175848.zip

image

ng version PS D:\projects\skyeng\skyeng-frontend\packages\vimbox-core\vimbox-boilerplate> yarn ng version yarn run v1.22.10 $ ng version
 _                      _                 ____ _     ___ 
/ \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|

/ △ \ | '_ \ / | | | | |/ _ | '__| | | | | | | / ___ | | | | (| | || | | (| | | | || | | | // __| ||_, |_,||_,|| _||| |___/

Angular CLI: 11.2.5 Node: 12.18.1 OS: win32 x64

Angular: 11.2.6 … animations, common, compiler, compiler-cli, core, elements … forms, language-service, localize, platform-browser
… platform-browser-dynamic, platform-server, router Ivy Workspace: Yes

Package Version

@angular-devkit/architect 0.1102.5 @angular-devkit/build-angular 0.1102.5 @angular-devkit/core 11.2.5 @angular-devkit/schematics 11.2.1 @angular/cdk 11.1.1 @angular/cli 11.2.5 @angular/flex-layout 11.0.0-beta.33 @angular/material 11.1.1 @ngtools/webpack 11.2.5 @nguniversal/builders 11.1.1 @nguniversal/express-engine 10.1.0 @schematics/angular 11.2.5 @schematics/update 0.1102.5 rxjs 6.6.2 typescript 4.1.3 webpack 4.46.0

Done in 1.09s.

@JoostK Still experiencing same performance problems with 11.2.5

Mode Cold Hot
NG_BUILD_IVY_LEGACY=1 ~120s ~10s
NG_BUILD_IVY_LEGACY=0 ~180s ~50s

I am having same problem and is not acceptable. My minimum recompilint time is 60 seconds.

Have the same issue, but it seems that regression is in 11.1.1 specifically. I started experiencing the slowness after upgrading from 11.1.0 -> 11.1.1.

After downgrading @angular-devkit/build-angular @angular/cli @angular/compiler-cli back to 11.1.0 slowness has disappeared.

For me incremental build results for the same page level edit are:

| Angular Cli 11.1.0 | Angular Cli 11.1.1 – | – | – lazy-module page level edit | ~5s | ~55s

@sod nope, we have a monorepo with a single root folder’s node_modules. I haven’t move code anywhere, updated libs, removed node_modules and installed it again , started devserver. Adding explicit types mapping does not help in any way.

Can confirm, the new plugin with 11.2.5 is super fast now. Unbelievable that you are working on your spare time on this @JoostK. Also thx to @clydin for improving the webpack integration.

oh, thx… i’ll wait for official release, to be sure that everything done right (also check that there is no getAllDependencies in profiler), and recheck than

@artaommahe It’s a bit confusing at the moment. Angular Framework 11.2.5 contains the framework’s contribution to the improved incremental rebuilds, which was released at the same time as Angular CLI 11.2.4 (Framework has had to do one additional patch release). We’ve now identified more improvements on the CLI side, so these will release in Angular CLI 11.2.5 next week. What I meant to say about the AOT profile you shared is that it is not using Angular Framework 11.2.5, which means that it does not have any of the improvements.

I guess with using NG_BUILD_IVY_LEGACY=1 you lose all benefits of Hot Module Replacement (which was my main reason for upgrading to Angular 11)

Checked new application (within the same monorepo) rebuild time for AppModule changes (added comment)

  • default mode 250-300ms

image

  • NG_BUILD_IVY_LEGACY=1 70-110ms

image

for the default mode there is a small compileStylesheet call righ after first processTimers block, i’ve added a scss files for the app.component, and it’s called even for an empty scss file. There is no such function call for old mode at all.