Skip to content
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

Sass Performance regression after update from v14 to v15 #26020

Closed
1 task done
JonWallsten opened this issue Nov 18, 2022 · 16 comments
Closed
1 task done

Sass Performance regression after update from v14 to v15 #26020

JonWallsten opened this issue Nov 18, 2022 · 16 comments
Labels
needs triage This issue needs to be triaged by the team perf This issue is related to performance

Comments

@JonWallsten
Copy link

JonWallsten commented Nov 18, 2022

Command

build

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

14.2.2

Description

I've just updated angular from 14.2.2 to 15.0.0 and I see increased build times with 50% 100% (15.0.4).
It seems related to SASS. I remember I had similar issues when upgrading to v12, but that time there was a migrator guide.
I did a CPU-profile, and it, together with all our scss files (except the components), is uploaded here:
https://github.com/JonWallsten/monorepo-new/tree/sass-debug/temp

We have multiple apps, but I uploaded one app and the shared library (built with ng-packagr) as an example.

Please note that the profile is not capturing the entire build since DevTools crashes when trying. But it's clear enough that SASS is the issue since the time it covers in the profile is the same time the full build dock in the previous version.

Chart: (The green color is sass-related)
image

Here's a "Bottom up" list from one of my profiles:
image
image

Please let me know if I can collect enable some other debugging information.

Minimal Reproduction

Since this happens in a complex setup with many files I'm not sure it would be easy to detect the issue with a few files. I think it's a situation where small thing adds up in the end.

Exception or Error

No response

Your Environment

Angular CLI: 15.0.0
Node: 18.12.0
Package Manager: npm 8.19.2
OS: win32 x64

Angular: 15.0.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, material, platform-browser, platform-browser-dynamic
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1500.0
@angular-devkit/build-angular   15.0.0
@angular-devkit/core            15.0.0
@angular-devkit/schematics      15.0.0
@ngtools/webpack                15.0.0
@schematics/angular             15.0.0
ng-packagr                      14.2.1
rxjs                            7.5.6
typescript                      4.8.3
webpack                         5.74.0

Anything else relevant?

We're using AngularWebpackPlugin to build.
And we have a monorepo setup with paths

new AngularWebpackPlugin({
    tsconfig: helpers.rootPath('tsconfig.build.json')
})

TSConfig

tsconfig.build.json

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
      "outDir": "./dist",
      "strictNullChecks": true,
      "noImplicitReturns": true,
      "noFallthroughCasesInSwitch": false,
      "noImplicitAny": true,
      "strictFunctionTypes": true,
      "strictBindCallApply": true
  },
  "exclude": [
    "../../build-tools/"
  ],
  "files": [
    "./src/main.ts",
    "./src/environments/environment.prod.ts",
    "./typings/global.d.ts",
    "../../typings/global.d.ts"
  ],
  "angularCompilerOptions": {
    "enableIvy": true,
    "strictTemplates": true,
    "disableTypeScriptVersionCheck": true
  }
}

../../tsconfig.json

{
  "compilerOptions": {
    "rootDir": ".",
    "baseUrl": ".",
    "target": "ES2020",
    "module": "CommonJS",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noEmitHelpers": true,
    "noEmitOnError": false,
    "importHelpers": true,
    "noImplicitAny": false,
    "skipLibCheck": true,
    "alwaysStrict": true,
    "strictNullChecks": false,
    "strictPropertyInitialization": false,
    "pretty": true,
    "sourceMap": true,
    "declaration": false,
    "preserveConstEnums": true,
    "downlevelIteration": true,
    "lib": [
      "dom",
      "ES2020"
    ],
    "paths": {
      "@oas/web-lib-angular": [
        "./packages/web-lib-angular/dist"
      ]
    }
  },
  "include": [
    "./build-tools/"
  ],
  "compileOnSave": false,
  "buildOnSave": false
}
@alan-agius4
Copy link
Contributor

alan-agius4 commented Nov 18, 2022

Hi @JonWallsten,

You mentioned that you are using AngularWebpackPlugin to compile your application, does this mean that you are not using the Angular CLI and have a custom webpack setup?

@JonWallsten
Copy link
Author

@alan-agius4 That is correct. I assumed the AngularWebpackPlugin was using the CLI in the background. But that might have been a bad guess from my side. Can you move the ticket to the repo where it belong?

@alan-agius4
Copy link
Contributor

alan-agius4 commented Nov 18, 2022

Can you move the ticket to the repo where it belong?

I did notice that you are using the async version of Sass

Maybe the components team increased the imports/use which could causing "expected" performance degradations when Sass is used in async mode, as these can be up to 2x slower than the sync version. Over the years in the Angular CLI we did several Sass compilations performance improvements that are typically caused by async compilations. However, since you are not using the Angular CLI to compile your application, fine tuning and optimizing the build is your responsibility.

Transferring to the material repo since they might have better knowledge on what changed, but since you are using your own build pipeline there is not much we can do here.

@devversion devversion transferred this issue from angular/angular-cli Nov 18, 2022
@JonWallsten
Copy link
Author

@alan-agius4 I hear that I need to read up on async/sync version of sass. Was not aware of that. Thanks!

@alan-agius4
Copy link
Contributor

https://sass-lang.com/documentation/js-api/

The JavaScript API provides two entrypoints for compiling Sass to CSS, each of which has a synchronous variant that returns a plain CompileResult and an asynchronous variant that returns a Promise. The asynchronous variants are much slower, but they allow custom importers and functions to run asynchronously.

renderSync runs synchronously. It's by far the fastest option when using Dart Sass, but at the cost of only supporting synchronous importer and function plugins.

https://sass-lang.com/documentation/js-api/modules#compileAsync

When using Dart Sass, compile is almost twice as fast as compileAsync, due to the overhead of making the entire evaluation process asynchronous.

@JonWallsten
Copy link
Author

JonWallsten commented Nov 18, 2022

Is the CLI using async or sync mode? The CLI is also using sass-loader, like we do. That would be my guess where to control if sass is async or sync?

Edit: Oh, I see that your using your own custom code for that.

@alan-agius4
Copy link
Contributor

@clydin, mentioned that the root cause is likely that in material version 15 has a significant amount of new imports due to the MDC change. With around 40 additional dependencies. This means that there are a lot more package resolution attempts when processing material stylesheets.

@JonWallsten
Copy link
Author

I haven't put any time in getting to know the processes behind the processing of sass in general since it usually just works. But now that it takes the majority of the build time I want to know how to improve the situationen. Is there anything I can do? Import less material modules? Skip theming? Or do I get everything anyways when using mat.core?

@JonWallsten
Copy link
Author

JonWallsten commented Nov 21, 2022

@alan-agius4 Using the synchronous version with sass-loader doesn't seem to be possible with Node >= 16.x.x.

image

Do I understand it correctly that you guys have you own importer/resolution code? How much faster is that compared to webpack's importer? You also have your own workers?

https://github.com/angular/angular-cli/blob/827fecceccdff7781a5ce27093163f29fbddd197/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts#L396

@alan-agius4
Copy link
Contributor

alan-agius4 commented Nov 21, 2022

Using the synchronous version with sass-loader doesn't seem to be possible with Node >= 16.x.x.

Yes, with sass-loader this is rather tricky and you do need to go out of your way to do it.

Do I understand it correctly that you guys have you own importer/resolution code? How much faster is that compared to webpack's importer? You also have your own workers?

Just to give some context, sass-loader by default uses the deprecated legacy Sass API, but in the CLI since version 15 we switched to use the modern API (which is also faster). sass-loader does not support Yarn PNP and PNPM when using Sass modern API therefore we created our own importer using Webpack's resolvers. So this is more than just performance improvement, but also adding support for things that sass-loader does not support.

We also use use workers do that each Sass compilation is non blocking while we force the resolvers to run in sync manner.

The Angular CLI integration is significantly faster, Example:

styles.scss

@use '@angular/material';
Build pipeline Timings
Angular CLI 9.761s
Webpack + Sass Loader 14.994s

The timings here are totally expected as Sass is around 50% slower when ran in async mode (webpack-contrib/sass-loader#701)

@JonWallsten
Copy link
Author

JonWallsten commented Nov 21, 2022

Thanks for the explanation!
After reading a bunch of threads on the subject and trying all the different option nothing had an impact on the build time.
I've tried webpackImporter: false and api: modern.

I tried out sass-emedded which is said to be faster for async operation, but I got stuck with this error:
image
Not sure why it has issues resolving the new @material/* packages.

Angular's implementation is exposed through the @angular-devkit, right? So I could potentially use the same solution the CLI is using? Ideally we would use the CLI, but legacy stuff is still stopping us.

@alan-agius4
Copy link
Contributor

With sass-loader, Sass compilation is always run in async mode. There is no option to force the sync behaviour.

sass-emedded is faster when used to compile large scss files like global css but it’s slower when used to compile smaller scss like components css. That was one of the reasons why in the CLI we do not use sass-embedded.

Angular's implementation is exposed through the @angular-devkit, right? So I could potentially use the same solution the CLI is using? Ideally we would use the CLI, but legacy stuff is still stopping us.

No, the implementation is private.

@JonWallsten
Copy link
Author

JonWallsten commented Nov 21, 2022

@alan-agius4 I see. Then I guess we're out of options and have to accept the 70% 100% in increased build time. Thanks for your time.

@alan-agius4 alan-agius4 added perf This issue is related to performance needs triage This issue needs to be triaged by the team labels Nov 23, 2022
@jelbourn jelbourn changed the title SASS Performance regression after update from v14 to v15 Sass Performance regression after update from v14 to v15 Dec 1, 2022
@JonWallsten
Copy link
Author

JonWallsten commented Dec 29, 2022

@alan-agius4: I've spent the morning catching up on all ongoing initiatives with speeding up SASS. Like:
sass/sass#3296
sass/dart-sass#868
And everything seems to be a dead end right now.
Our build times are up around 100% when compiling all our applications. The main application is upp around 300%.
Of course it's hard to accept the situation, but we can't just stop updating Angular.

So, since I'm out of alternatives I've copied to files from angular-cli/angular_devkit/build_angular/sass/ and removed everything we don't need, like the legacy implementation. The build time went from 171s to 52s for our main application (haven't tested with all apps yet).
Are there any plans of making these tools available for AngularCompilerPlugin users such as ourselves?
I don't mind keeping up to date by copying files since the price to pay is still small compared to the gain in build times.

@andrewseguin
Copy link
Contributor

It's unfortunate to hear your build times are slower using the repo, and it is likely due to the increased deps on MDC. In this case it doesn't look like there's anything we can do to help

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jun 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
needs triage This issue needs to be triaged by the team perf This issue is related to performance
Projects
None yet
Development

No branches or pull requests

3 participants