SASS Performance regression after update from v14 to v15
Command
build
Is this a regression?
- [X] 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%.
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)

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

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
}
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?
@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?
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.
@alan-agius4 I hear that I need to read up on async/sync version of sass. Was not aware of that. Thanks!
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.
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.
@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.
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?
@alan-agius4 Using the synchronous version with sass-loader doesn't seem to be possible with Node >= 16.x.x.

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
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 (https://github.com/webpack-contrib/sass-loader/issues/701)
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:
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.
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.
@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: I've spent the morning catching up on all ongoing initiatives with speeding up SASS. Like: https://github.com/sass/sass/issues/3296 https://github.com/sass/dart-sass/issues/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.
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
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.