-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Significant Slowdown in M3 Runtime Compilation #28971
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
Comments
I believe that Nx uses some sort of build caching. Do you see these slowdowns consistently or only on the initial build? Also what changes did you make to enable M3? We export all the APIs through the same |
@crisbeto
Nx 18, 19 before m3 and after m3 build times and runtimes doubled up |
Can you also post what your theme looked like for M2? |
Mitigates a compile time regression when generating M3 themes. These changes reduce the compilation time in half by caching the dummy theme instead of recreating it for each invocation. We can get away with this since the dummy theme is constant. Although these changes are a significant improvement, there's more room for improvement. Timings for reference: At head: ``` M2 benchmark - 35s M3 benchmark - 90s Theme from angular#28971 - 19s ``` After these changes changes: ``` M2 benchmark - 36s M3 benchmark - 56s Theme from angular#28971 - 10s ``` Relates to angular#28971.
Mitigates a compile time regression when generating M3 themes. These changes reduce the compilation time in half by caching the dummy theme instead of recreating it for each invocation. We can get away with this since the dummy theme is constant. Although these changes are a significant improvement, there's more room for improvement. Timings for reference: At head: ``` M2 benchmark - 35s M3 benchmark - 90s Theme from angular#28971 - 19s ``` After these changes changes: ``` M2 benchmark - 36s M3 benchmark - 56s Theme from angular#28971 - 10s ``` Relates to angular#28971.
I've sent out #29009 which seems to cut the compilation time at least in half for M3. |
Mitigates a compile time regression when generating M3 themes. These changes reduce the compilation time in half by caching the dummy theme instead of recreating it for each invocation. We can get away with this since the dummy theme is constant. Although these changes are a significant improvement, there's more room for improvement. Timings for reference: At head: ``` M2 benchmark - 35s M3 benchmark - 90s Theme from #28971 - 19s ``` After these changes changes: ``` M2 benchmark - 36s M3 benchmark - 56s Theme from #28971 - 10s ``` Relates to #28971.
Mitigates a compile time regression when generating M3 themes. These changes reduce the compilation time in half by caching the dummy theme instead of recreating it for each invocation. We can get away with this since the dummy theme is constant. Although these changes are a significant improvement, there's more room for improvement. Timings for reference: At head: ``` M2 benchmark - 35s M3 benchmark - 90s Theme from #28971 - 19s ``` After these changes changes: ``` M2 benchmark - 36s M3 benchmark - 56s Theme from #28971 - 10s ``` Relates to #28971. (cherry picked from commit 0188516)
Im adding my entire m3 and m2 styles @crisbeto M3
M2
|
@crisbeto i just tested with 18 rc1 with m3 its 20s with all the esbuild and latest stack should be always faster compared to older versions everytime you compile the app it feels like running a java application it takes forever |
I agree that the timing still isn't ideal, I just wasn't able to find more ways in which to cut it down. Sass doesn't have APIs to profile the speed of the various function calls so it's lots of trial and error. |
I don't use Nx and my project's dev builder has slowed down from 20~ seconds to 40-70 seconds (it's worse when there are styles changes) after upgrading to Angular 18 and adjusting to the new SCSS API Edit: I no longer have this issue |
Recently, I investigated the slow build times of our enterprise app. After upgrading Angular and Angular Material to v19, I noticed a significant increase in the build times (although the app had been slightly slower even before the v19 upgrade). I migrated the app to the new esbuild-based build system, but this did not resolve the issue. Digging deeper, I enabled all possible logs and found that the bulk of the build time was being spent on Sass compilation. I decided to add custom metrics to measure this process more precisely by plugging into Angular's Sass esbuild plugin. It turned out that every To optimize this, I started removing these imports from individual component stylesheets. Instead, I extracted the values I needed from Here’s an example of a change I made: Before: @use '@angular/material' as mat;
.mat-dense-list {
@include mat.list-density(-4);
} After: @use '@angular/material/list/list-theme' as matListTheme;
.mat-dense-list {
@include matListTheme.density(-4);
} I know this is not an official method for using Angular Material’s Sass styles, but the official approach is just too slow for practical use in a large application like ours. By using "deep" imports and carefully managing the dependencies in shared Sass files, I was able to achieve much faster build times without impacting the functionality of the components. However, I acknowledge that relying on internal or non-documented APIs isn’t ideal for long-term maintainability. This optimization led to a dramatic improvement in my CI build times: I went from 10 minutes down to 4 minutes (!) just by modifying how the Sass imports were managed. I hope this helps anyone else struggling with the same issue! I created a patch-package modification to the Angular build tools. This allowed me to measure the processing time for each Sass file, which helped me detect slow compilation files. If anyone is interested, here is the patch code I used: diff --git a/node_modules/@angular/build/src/tools/sass/sass-service.js b/node_modules/@angular/build/src/tools/sass/sass-service.js
index 927b565..2880e8e 100644
--- a/node_modules/@angular/build/src/tools/sass/sass-service.js
+++ b/node_modules/@angular/build/src/tools/sass/sass-service.js
@@ -81,13 +81,19 @@ const MAX_RENDER_WORKERS = environment_options_1.maxWorkers;
* with the `dart-sass` package. The `dart-sass` synchronous render function is used within
* the worker which can be up to two times faster than the asynchronous variant.
*/
+
+
class SassWorkerImplementation {
rebase;
maxThreads;
#workerPool;
+ numberOfProcessed;
+ numberOfSlow;
constructor(rebase = false, maxThreads = MAX_RENDER_WORKERS) {
this.rebase = rebase;
this.maxThreads = maxThreads;
+ this.numberOfProcessed = 0;
+ this.numberOfSlow = 0;
}
#ensureWorkerPool() {
this.#workerPool ??= new worker_pool_1.WorkerPool({
@@ -117,6 +123,7 @@ class SassWorkerImplementation {
*/
async compileStringAsync(source, options) {
const env_1 = { stack: [], error: void 0, hasError: false };
+
try {
// The `functions`, `logger` and `importer` options are JavaScript functions that cannot be transferred.
// If any additional function options are added in the future, they must be excluded as well.
@@ -160,6 +167,17 @@ class SassWorkerImplementation {
throw error;
}
(0, node_assert_1.default)(result, 'Sass render worker should always return a result or an error');
+
+ this.numberOfProcessed = this.numberOfProcessed + 1;
+
+ const resultTime = response.processingTime;
+ if (resultTime > 500) {
+ this.numberOfSlow = this.numberOfSlow + 1;
+ console.log('[SLOW]', options.url.pathname, `${resultTime}ms`);
+ }
+
+ console.log(`${this.numberOfSlow} of ${this.numberOfProcessed} is slow`);
+
return {
...result,
// URL is not serializable so in the worker we convert to string and here back to URL.
diff --git a/node_modules/@angular/build/src/tools/sass/worker.js b/node_modules/@angular/build/src/tools/sass/worker.js
index 2247553..9157528 100644
--- a/node_modules/@angular/build/src/tools/sass/worker.js
+++ b/node_modules/@angular/build/src/tools/sass/worker.js
@@ -17,6 +17,7 @@ const node_url_1 = require("node:url");
const node_worker_threads_1 = require("node:worker_threads");
const sass_1 = require("sass");
const rebasing_importer_1 = require("./rebasing-importer");
+
async function renderSassStylesheet(request) {
const { importerChannel, hasLogger, source, options, rebase } = request;
const entryDirectory = (0, node_path_1.dirname)(options.url);
@@ -80,6 +81,7 @@ async function renderSassStylesheet(request) {
relativeImporter = (0, rebasing_importer_1.sassBindWorkaround)(new rebasing_importer_1.RelativeUrlRebasingImporter(entryDirectory, directoryCache, rebaseSourceMaps));
}
// The synchronous Sass render function can be up to two times faster than the async variant
+ const resultStartTime = performance.now();
const result = (0, sass_1.compileString)(source, {
...options,
// URL is not serializable so to convert to string in the parent and back to URL here.
@@ -102,6 +104,10 @@ async function renderSassStylesheet(request) {
}
: undefined,
});
+
+ const resultEndTime = performance.now();
+ const processingTime = (resultEndTime - resultStartTime).toFixed(2);
+
if (result.sourceMap && rebaseSourceMaps?.size) {
// Merge the intermediate rebasing source maps into the final Sass generated source map.
// Casting is required due to small but compatible differences in typings between the packages.
@@ -110,8 +116,10 @@ async function renderSassStylesheet(request) {
// is referencing its original self.
(file, context) => (file !== context.importer ? rebaseSourceMaps.get(file) : null));
}
+
return {
warnings,
+ processingTime,
result: {
...result,
sourceMap: result.sourceMap, I hope this helps anyone interested in debugging or resolving similar issues with performance in Angular projects. |
We very recently significantly refactored how we build theme configs. It would be helpful if some of you could try checking whether the build times improved as a result. Once this PR lands, the build should be better: #31138 It should be included in the next |
Is this a regression?
The previous version in which this bug was not present was
No response
Description
Building our NX monorepo, particularly when utilizing M3 material components at runtime, is experiencing noticeable delays. This slowdown in compilation time is impacting our development efficiency.
Without M3, the compilation process takes approximately 2 seconds. However, with M3 integrated, the compilation time extends to 40 seconds or more.
Reproduction
StackBlitz link:
Steps to reproduce:
1.
2.
Expected Behavior
.
Actual Behavior
.
Environment
The text was updated successfully, but these errors were encountered: