Skip to content

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

Open
1 task
flipcasa opened this issue Apr 26, 2024 · 10 comments
Open
1 task

Significant Slowdown in M3 Runtime Compilation #28971

flipcasa opened this issue Apr 26, 2024 · 10 comments
Labels
area: material/core M3 needs investigation A member of the team needs to do further investigation to determine the root cause P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Comments

@flipcasa
Copy link

flipcasa commented Apr 26, 2024

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

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

  • Angular: v18.0.0-rc.0
  • CDK/Material: v18.0.0-rc.0
  • Browser(s): chrome
  • Operating System (e.g. Windows, macOS, Ubuntu): mac
@flipcasa flipcasa added the needs triage This issue needs to be triaged by the team label Apr 26, 2024
@crisbeto
Copy link
Member

crisbeto commented May 6, 2024

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 _index.scss so I would expect the build graph to look identical no matter what version of Material you're using.

@amysorto amysorto added needs: clarification The issue does not contain enough information for the team to determine if it is a real bug and removed needs triage This issue needs to be triaged by the team labels May 6, 2024
@flipcasa
Copy link
Author

flipcasa commented May 6, 2024

@crisbeto
slowdown is consistent

@use '@angular/material' as mat;
@use 'sass:map';

mat.$theme-legacy-inspection-api-compatibility: false;

// Create a theme with the specified color type and density.
@function create-theme($type: light, $density: 0, $primary: mat.$blue-palette, $tertiary: mat.$violet-palette) {
  @return mat.define-theme(
    (
      color: (
        theme-type: $type,
        primary: $primary,
        tertiary: $tertiary
      ),
      density: (
        scale: $density
      ),
      typography: (
        brand-family: 'Be Vietnam Pro',
        plain-family: 'sans-serif',
        // regular-weight: 400,
        // medium-weight: 500,
        // bold-weight: 700
      )
    )
  );
}

// Define the default (light) theme.
$light-theme: create-theme(
  $type: light
);

// Create our dark theme.
$dark-theme: create-theme(
  $type: dark
);

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat.core();

// Include the default theme styles.
html {
  @include mat.all-component-themes($light-theme);
}
@include mat.typography-hierarchy($light-theme);

.dark-theme {
  // Include the dark theme color styles.
  @include mat.all-component-colors($dark-theme);
}

// Create classes for all density scales which are supported by all MDC-based components.
// The classes are applied conditionally based on the selected density in the dev-app layout
// component.
$density-scales: (0, -1, -2, -3, -4);
@each $scale in $density-scales {
  .density-#{$scale} {
    $density-theme: create-theme(
      $density: $scale
    );
    @include mat.all-component-densities($density-theme);
  }
}

.default {
  $light-theme: create-theme(
    $type: light,
    $primary: mat.$azure-palette,
    $tertiary: mat.$blue-palette
  );

  // Create our dark theme.
  $dark-theme: create-theme(
    $type: dark,
    $primary: mat.$azure-palette,
    $tertiary: mat.$blue-palette
  );

  // Theme Config
  @include mat.all-component-colors($light-theme);

  &.dark-theme {
    @include mat.all-component-colors($dark-theme);
  }

  // Enable backwards-compatibility CSS for color="..." API & typography hierarchy.
  &.back-compat {
    @include mat.color-variants-backwards-compatibility($light-theme);
    @include mat.typography-hierarchy($light-theme, $back-compat: true);

    &.dark-theme {
      @include mat.color-variants-backwards-compatibility($dark-theme);
    }
  }
}

Nx 18, 19
Angular 18 rc 0 even with experimental 17 package

before m3 and after m3 build times and runtimes doubled up

@crisbeto crisbeto added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent needs investigation A member of the team needs to do further investigation to determine the root cause area: material/core M3 and removed needs: clarification The issue does not contain enough information for the team to determine if it is a real bug labels May 7, 2024
@crisbeto
Copy link
Member

crisbeto commented May 7, 2024

Can you also post what your theme looked like for M2?

crisbeto added a commit to crisbeto/material2 that referenced this issue May 7, 2024
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.
crisbeto added a commit to crisbeto/material2 that referenced this issue May 7, 2024
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.
@crisbeto
Copy link
Member

crisbeto commented May 7, 2024

I've sent out #29009 which seems to cut the compilation time at least in half for M3.

crisbeto added a commit that referenced this issue May 7, 2024
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.
crisbeto added a commit that referenced this issue May 7, 2024
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)
@flipcasa
Copy link
Author

flipcasa commented May 7, 2024

Can you also post what your theme looked like for M2?

Im adding my entire m3 and m2 styles @crisbeto

M3

@use '@angular/material' as mat;
@use 'sass:map';

mat.$theme-legacy-inspection-api-compatibility: false;

// Create a theme with the specified color type and density.
@function create-theme($type: light, $density: 0, $primary: mat.$blue-palette, $tertiary: mat.$violet-palette) {
  @return mat.define-theme(
    (
      color: (
        theme-type: $type,
        primary: $primary,
        tertiary: $tertiary
      ),
      density: (
        scale: $density
      ),
      typography: (
        brand-family: 'Be Vietnam Pro',
        plain-family: 'sans-serif',
        // regular-weight: 400,
        // medium-weight: 500,
        // bold-weight: 700
      )
    )
  );
}

// Define the default (light) theme.
$light-theme: create-theme(
  $type: light
);

// Create our dark theme.
$dark-theme: create-theme(
  $type: dark
);

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// **Be sure that you only ever include this mixin once!**
@include mat.core();

// Include the default theme styles.
html {
  @include mat.all-component-themes($light-theme);
}
@include mat.typography-hierarchy($light-theme);

.dark-theme {
  // Include the dark theme color styles.
  @include mat.all-component-colors($dark-theme);
}

// Create classes for all density scales which are supported by all MDC-based components.
// The classes are applied conditionally based on the selected density in the dev-app layout
// component.
$density-scales: (0, -1, -2, -3, -4);
@each $scale in $density-scales {
  .density-#{$scale} {
    $density-theme: create-theme(
      $density: $scale
    );
    @include mat.all-component-densities($density-theme);
  }
}

.theme-default {
  $light-theme: create-theme(
    $type: light,
    $primary: mat.$azure-palette,
    $tertiary: mat.$blue-palette
  );

  // Create our dark theme.
  $dark-theme: create-theme(
    $type: dark,
    $primary: mat.$azure-palette,
    $tertiary: mat.$blue-palette
  );

  // Theme Config
  @include mat.all-component-colors($light-theme);

  &.dark-theme {
    @include mat.all-component-colors($dark-theme);
  }

  // Enable backwards-compatibility CSS for color="..." API & typography hierarchy.
  &.back-compat {
    @include mat.color-variants-backwards-compatibility($light-theme);
    @include mat.typography-hierarchy($light-theme, $back-compat: true);

    &.dark-theme {
      @include mat.color-variants-backwards-compatibility($dark-theme);
    }
  }
}

.theme-rose {
  // Define the default (light) theme.
  $light-theme: create-theme(
    $type: light,
    $primary: mat.$rose-palette,
    $tertiary: mat.$red-palette
  );

  // Create our dark theme.
  $dark-theme: create-theme(
    $type: dark,
    $primary: mat.$rose-palette,
    $tertiary: mat.$red-palette
  );

  // Theme Config
  @include mat.all-component-colors($light-theme);

  &.dark-theme {
    @include mat.all-component-colors($dark-theme);
  }

  // Enable backwards-compatibility CSS for color="..." API & typography hierarchy.
  &.back-compat {
    @include mat.color-variants-backwards-compatibility($light-theme);
    @include mat.typography-hierarchy($light-theme, $back-compat: true);

    &.dark-theme {
      @include mat.color-variants-backwards-compatibility($dark-theme);
    }
  }
}

.theme-cyan {
  // Define the default (light) theme.
  $light-theme: create-theme(
    $type: light,
    $primary: mat.$cyan-palette,
    $tertiary: mat.$orange-palette
  );

  // Create our dark theme.
  $dark-theme: create-theme(
    $type: dark,
    $primary: mat.$cyan-palette,
    $tertiary: mat.$orange-palette
  );

  // Theme Config
  @include mat.all-component-colors($light-theme);

  &.dark-theme {
    @include mat.all-component-colors($dark-theme);
  }

  // Enable backwards-compatibility CSS for color="..." API & typography hierarchy.
  &.back-compat {
    @include mat.color-variants-backwards-compatibility($light-theme);
    @include mat.typography-hierarchy($light-theme, $back-compat: true);

    &.dark-theme {
      @include mat.color-variants-backwards-compatibility($dark-theme);
    }
  }
}

.theme-magenta {
  // Define the default (light) theme.
  $light-theme: create-theme(
    $type: light,
    $primary: mat.$magenta-palette,
    $tertiary: mat.$violet-palette
  );

  // Create our dark theme.
  $dark-theme: create-theme(
    $type: dark,
    $primary: mat.$magenta-palette,
    $tertiary: mat.$violet-palette
  );

  // Theme Config
  @include mat.all-component-colors($light-theme);

  &.dark-theme {
    @include mat.all-component-colors($dark-theme);
  }

  // Enable backwards-compatibility CSS for color="..." API & typography hierarchy.
  &.back-compat {
    @include mat.color-variants-backwards-compatibility($light-theme);
    @include mat.typography-hierarchy($light-theme, $back-compat: true);

    &.dark-theme {
      @include mat.color-variants-backwards-compatibility($dark-theme);
    }
  }
}

M2

@use '@angular/material' as mat;

mat.$theme-ignore-duplication-warnings: true;

// Light Theme Text
$dark-text: #000000;
$dark-primary-text: rgba($dark-text, 0.87);
$dark-accent-text: rgba($dark-primary-text, 0.54);

// Dark Theme text
$light-text: #ffffff;
$light-primary-text: $light-text;
$light-accent-text: rgba($light-primary-text, 0.7);

@include mat.core();

.theme-core {
  // Compute font config
  // @include mat.core($fontConfig);
  $mat-primary: (
    main: #115df6,
    lighter: #f0f5fe,
    darker: #0a41f2,
    200: #115df6,
    // For slide toggle,
    contrast:
      (
        main: $light-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-primary: mat.define-palette($mat-primary, main, lighter, darker);
  $mat-accent: (
    main: #042a76,
    lighter: #dbe7fe,
    darker: #021959,
    200: #042a76,
    // For slide toggle,
    contrast:
      (
        main: $light-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-accent: mat.define-palette($mat-accent, main, lighter, darker);
  $mat-warn: (
    main: #d17823,
    lighter: #f8dbd8,
    darker: #c05b15,
    200: #d17823,
    // For slide toggle,
    contrast:
      (
        main: $dark-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-warn: mat.define-palette($mat-warn, main, lighter, darker);

  --primary-color: #115df6;
  --primary-lighter-color: #f0f5fe;
  --primary-darker-color: #0a41f2;
  --text-primary-color: #{$light-primary-text};
  --text-primary-lighter-color: #{$dark-primary-text};
  --text-primary-darker-color: #{$light-primary-text};

  --accent-color: #042a76;
  --accent-lighter-color: #dbe7fe;
  --accent-darker-color: #021959;
  // --text-accent-color: #{$light-accent-text};
  // --text-accent-lighter-color: #{$dark-accent-text};
  // --text-accent-darker-color: #{$light-accent-text};

  --warn-color: #d17823;
  --warn-lighter-color: #f8dbd8;
  --warn-darker-color: #c05b15;
  --text-warn-color: #{$dark-primary-text};
  --text-warn-lighter-color: #{$dark-primary-text};
  --text-warn-darker-color: #{$light-primary-text};

  // Theme Config
  $typography: mat.define-typography-config(
    $font-family: 'Be Vietnam Pro',
    $headline-1: mat.define-typography-level(112px, 112px, 300, $letter-spacing: -0.0134em),
    $headline-2: mat.define-typography-level(56px, 56px, 400, $letter-spacing: -0.0089em),
    $headline-3: mat.define-typography-level(45px, 48px, 400, $letter-spacing: 0em),
    $headline-4: mat.define-typography-level(34px, 40px, 400, $letter-spacing: 0.0074em),
    $headline-5: mat.define-typography-level(24px, 32px, 400, $letter-spacing: 0em),
    $headline-6: mat.define-typography-level(20px, 32px, 500, $letter-spacing: 0.0075em),
    $subtitle-1: mat.define-typography-level(16px, 28px, 400, $letter-spacing: 0.0094em),
    $subtitle-2: mat.define-typography-level(15px, 24px, 500, $letter-spacing: 0.0067em),
    $body-2: mat.define-typography-level(14px, 24px, 500, $letter-spacing: 0.0179em),
    $body-1: mat.define-typography-level(14px, 20px, 400, $letter-spacing: 0.0179em),
    $button: mat.define-typography-level(14px, 14px, 500, $letter-spacing: 0.0893em),
    $caption: mat.define-typography-level(12px, 20px, 400, $letter-spacing: 0.0333em)
  );

  $theme: mat.define-light-theme(
    (
      color: (
        primary: $theme-primary,
        accent: $theme-accent,
        warn: $theme-warn
      ),
      typography: $typography,
      density: 0
    )
  );
  $altTheme: mat.define-dark-theme(
    (
      color: (
        primary: $theme-primary,
        accent: $theme-accent,
        warn: $theme-warn
      ),
      typography: $typography,
      density: 0
    )
  );

  @include mat.all-component-themes($theme);

  &.dark-theme {
    @include mat.all-component-colors($altTheme);
  }
}

.theme-dev {
  // Compute font config
  // @include mat.core($fontConfig);
  $mat-primary: (
    main: #b71c1c,
    lighter: #e9bbbb,
    darker: #9f1111,
    200: #b71c1c,
    // For slide toggle,
    contrast:
      (
        main: $light-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-primary: mat.define-palette($mat-primary, main, lighter, darker);
  $mat-accent: (
    main: #797979,
    lighter: #d7d7d7,
    darker: #5c5c5c,
    200: #797979,
    // For slide toggle,
    contrast:
      (
        main: $light-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-accent: mat.define-palette($mat-accent, main, lighter, darker);
  $mat-warn: (
    main: #ff0000,
    lighter: #ffb3b3,
    darker: #ff0000,
    200: #ff0000,
    // For slide toggle,
    contrast:
      (
        main: $dark-primary-text,
        lighter: $dark-primary-text,
        darker: $light-primary-text
      )
  );
  $theme-warn: mat.define-palette($mat-warn, main, lighter, darker);

  --primary-color: #b71c1c;
  --primary-lighter-color: #e9bbbb;
  --primary-darker-color: #9f1111;
  --text-primary-color: #{$light-primary-text};
  --text-primary-lighter-color: #{$dark-primary-text};
  --text-primary-darker-color: #{$light-primary-text};

  --accent-color: #797979;
  --accent-lighter-color: #d7d7d7;
  --accent-darker-color: #5c5c5c;
  // --text-accent-color: #{$light-accent-text};
  // --text-accent-lighter-color: #{$dark-accent-text};
  // --text-accent-darker-color: #{$light-accent-text};

  --warn-color: #ff0000;
  --warn-lighter-color: #ffb3b3;
  --warn-darker-color: #ff0000;
  --text-warn-color: #{$light-primary-text};
  --text-warn-lighter-color: #{$dark-primary-text};
  --text-warn-darker-color: #{$light-primary-text};

  // Theme Config
  $typography: mat.define-typography-config(
    $font-family: 'Be Vietnam Pro',
    $headline-1: mat.define-typography-level(112px, 112px, 300, $letter-spacing: -0.0134em),
    $headline-2: mat.define-typography-level(56px, 56px, 400, $letter-spacing: -0.0089em),
    $headline-3: mat.define-typography-level(45px, 48px, 400, $letter-spacing: 0em),
    $headline-4: mat.define-typography-level(34px, 40px, 400, $letter-spacing: 0.0074em),
    $headline-5: mat.define-typography-level(24px, 32px, 400, $letter-spacing: 0em),
    $headline-6: mat.define-typography-level(20px, 32px, 500, $letter-spacing: 0.0075em),
    $subtitle-1: mat.define-typography-level(16px, 28px, 400, $letter-spacing: 0.0094em),
    $subtitle-2: mat.define-typography-level(15px, 24px, 500, $letter-spacing: 0.0067em),
    $body-2: mat.define-typography-level(14px, 24px, 500, $letter-spacing: 0.0179em),
    $body-1: mat.define-typography-level(14px, 20px, 400, $letter-spacing: 0.0179em),
    $button: mat.define-typography-level(14px, 14px, 500, $letter-spacing: 0.0893em),
    $caption: mat.define-typography-level(12px, 20px, 400, $letter-spacing: 0.0333em)
  );

  $theme: mat.define-light-theme(
    (
      color: (
        primary: $theme-primary,
        accent: $theme-accent,
        warn: $theme-warn
      ),
      typography: $typography,
      density: 0
    )
  );
  $altTheme: mat.define-dark-theme(
    (
      color: (
        primary: $theme-primary,
        accent: $theme-accent,
        warn: $theme-warn
      ),
      typography: $typography,
      density: 0
    )
  );

  @include mat.all-component-themes($theme);

  &.dark-theme {
    @include mat.all-component-colors($altTheme);
  }
}

@flipcasa
Copy link
Author

flipcasa commented May 9, 2024

@crisbeto i just tested with 18 rc1
its better now it reduced to 20s but its still a lot

with m3 its 20s
with m2 its 7s

with all the esbuild and latest stack should be always faster compared to older versions
seems like its other way around

everytime you compile the app it feels like running a java application it takes forever

@crisbeto
Copy link
Member

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.

@LuisTovar0
Copy link

LuisTovar0 commented May 27, 2024

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

@michalma
Copy link

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 @use '@angular/material' as mat in Sass introduced a slowdown of approximately 1.5 to 3 seconds per file containing that import. Since my app contained many such imports at the component stylesheet level, this accumulation resulted in a substantial delay.

To optimize this, I started removing these imports from individual component stylesheets. Instead, I extracted the values I needed from @angular/material into variables and cached them in shared Sass files (e.g., for color, typography, density customizations). Additionally, within my shared Sass stylesheets, I replaced direct imports of the entire @angular/material package with more specific, "deep" imports. This change substantially improved Sass compilation times.

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.

@andrewseguin
Copy link
Contributor

andrewseguin commented May 16, 2025

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 v20 release next Wednesday

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/core M3 needs investigation A member of the team needs to do further investigation to determine the root cause P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests

6 participants