[angular/angular-cli] perf(@angular/build): use native sass-embedded compiler daemon (PR #33935)
DRANK
Replaces the Piscina-based worker thread pool executing pure-JS Dart Sass (`sass` compiled via `dart2js`) with a native Dart Sass asynchronous compiler daemon (`sass-embedded`). The build system previously relied on multi-threaded worker pools with synchronous compilation to bypass performance overheads inherent to pure-JS asynchronous Dart Sass. However, this approach incurred significant V8 heap and isolate duplication across worker threads, as well as thread contention and blocking due to `Atomics.wait()` synchronization loops during custom URL and module rebasing. With `sass-embedded`, Sass compilation is delegated to a long-lived native Dart AOT binary communicating asynchronously via Protocol Buffers over stdio pipes. This eliminates duplicate V8 isolates, replaces synchronous `Atomics.wait()` with event-driven asynchronous message handling, and dramatically reduces memory pressure on the Node.js process. The compiler infrastructure is structured around a shared `SassServiceImplementation` interface with two isolated implementations dynamically loaded on demand: - `SassAsyncCompilerImplementation`: Manages the native `sass-embedded` daemon lifecycle and asynchronous URL rebasing importers (`AsyncModuleUrlRebasingImporter`). - `SassWorkerImplementation`: Retained as an isolated worker pool fallback. ### Environment Variable & Fallbacks - `NG_BUILD_SASS_WORKER=1` (or `true`): Forces the build system to fall back to the pure-JS `SassWorkerImplementation` using worker threads. - In restricted environments where native platform binaries cannot be spawned (such as WebContainers detected via `process.versions.webcontainer`), the build system automatically falls back to `SassWorkerImplementation` without requiring manual configuration. ### Benchmarks: Sass Service in Isolation #### 10 Small Files (Angular Components) | Metric | Worker Pool (`main`) | Native Async (`this change`) | Delta | Speedup / Improvement | | :--- | :---: | :---: | :---: | :---: | | Cold Start | 369.04 ms | 229.89 ms | -139.15 ms | 1.61x faster (-37.7%) | | Warm Median | 22.46 ms | 5.27 ms | -17.19 ms | 4.26x faster (-76.5%) | | Warm Mean | 25.82 ms | 7.25 ms | -18.57 ms | 3.56x faster (-71.9%) | | Warm P95 | 37.30 ms | 14.55 ms | -22.75 ms | 2.56x faster (-61.0%) | | Warm Throughput | 445.2 files/s | 1,899.3 files/s | +1,454.1 files/s | 4.27x throughput (+326.6%) | | Incremental Median | 6.54 ms | 2.08 ms | -4.46 ms | 3.14x faster (-68.2%) | | Incremental Throughput | 153.0 files/s | 480.5 files/s | +327.5 files/s | 3.14x throughput (+214.0%) | | Peak Memory RSS | 281.16 MB | 78.73 MB | -202.43 MB | -72.0% memory saved | #### 10 Large Files (Design System Tokens & Themes) | Metric | Worker Pool (`main`) | Native Async (`this change`) | Delta | Speedup / Improvement | | :--- | :---: | :---: | :---: | :---: | | Cold Start | 823.95 ms | 386.25 ms | -437.70 ms | 2.13x faster (-53.1%) | | Warm Median | 231.85 ms | 175.64 ms | -56.21 ms | 1.32x faster (-24.2%) | | Warm Mean | 241.50 ms | 172.22 ms | -69.28 ms | 1.40x faster (-28.7%) | | Warm P95 | 293.09 ms | 215.48 ms | -77.61 ms | 1.36x faster (-26.5%) | | Warm Throughput | 43.1 files/s | 56.9 files/s | +13.8 files/s | 1.32x throughput (+32.0%) | | Incremental Median | 72.63 ms | 35.34 ms | -37.29 ms | 2.06x faster (-51.3%) | | Incremental Throughput | 13.8 files/s | 28.3 files/s | +14.5 files/s | 2.05x throughput (+105.4%) | | Peak Memory RSS | 986.47 MB | 112.34 MB | -874.13 MB | -88.6% memory saved | ### Benchmarks: Real Angular CLI Application Scenarios (ng build) #### Small App (5 small files) | Phase / Metric | Main (`NG_BUILD_SASS_WORKER=1`) | This Change (Native Async) | Delta | Improvement | | :--- | :---: | :---: | :---: | :---: | | Cold Wall Time | 3,201.3 ms | 2,950.2 ms | -251.1 ms | -7.8% | | Cold CLI Time | 2,456.0 ms | 2,302.0 ms | -154.0 ms | -6.3% | | Cold Peak RSS | 1,227.3 MB | 1,058.9 MB | -168.4 MB | -13.7% | | Warm (Avg) Wall Time | 2,762.0 ms | 2,475.3 ms | -286.7 ms | -10.4% | | Warm (Avg) CLI Time | 2,071.3 ms | 1,820.3 ms | -251.0 ms | -12.1% | | Warm (Avg) Peak RSS | 1,102.3 MB | 894.8 MB | -207.5 MB | -18.8% | #### Small App (5 large files) | Phase / Metric | Main (`NG_BUILD_SASS_WORKER=1`) | This Change (Native Async) | Delta | Improvement | | :--- | :---: | :---: | :---: | :---: | | Cold Wall Time | 3,605.9 ms | 3,175.8 ms | -430.1 ms | -11.9% | | Cold CLI Time | 2,874.0 ms | 2,482.0 ms | -392.0 ms | -13.6% | | Cold Peak RSS | 1,420.2 MB | 1,072.3 MB | -347.9 MB | -24.5% | | Warm (Avg) Wall Time | 3,123.7 ms | 2,682.5 ms | -441.2 ms | -14.1% | | Warm (Avg) CLI Time | 2,435.7 ms | 2,028.7 ms | -407.0 ms | -16.7% | | Warm (Avg) Peak RSS | 1,278.2 MB | 911.3 MB | -366.9 MB | -28.7% | #### Large App (500 files: 250 large + 250 small) | Phase / Metric | Main (`NG_BUILD_SASS_WORKER=1`) | This Change (Native Async) | Delta | Improvement | | :--- | :---: | :---: | :---: | :---: | | Cold Wall Time | 15,259.7 ms | 15,348.6 ms | +88.9 ms | +0.6% (Parity) | | Cold CLI Time | 14,425.0 ms | 14,373.0 ms | -52.0 ms | -0.4% (Parity) | | Cold Peak RSS | 2,305.1 MB | 1,582.7 MB | -722.4 MB | -31.3% memory saved | | Warm Run 1 Wall Time | 14,673.1 ms | 14,277.1 ms | -396.0 ms | -2.7% | | Warm Run 1 CLI Time | 13,812.0 ms | 13,375.0 ms | -437.0 ms | -3.2% | | Warm Run 1 Peak RSS | 2,180.5 MB | 1,485.6 MB | -694.9 MB | -31.9% memory saved | | Warm Run 2 Wall Time | 14,771.2 ms | 14,502.7 ms | -268.5 ms | -1.8% | | Warm Run 2 CLI Time | 13,943.0 ms | 13,527.0 ms | -416.0 ms | -3.0% | | Warm Run 2 Peak RSS | 2,220.8 MB | 1,493.3 MB | -727.5 MB | -32.8% memory saved | | Warm (Avg) Peak RSS | 2,200.7 MB | 1,489.5 MB | -711.2 MB | -32.3% memory saved | Closes #29319
🔖 "perf(@angular/build): use native sass-embedded compiler daemon by alan-agius4 · Pull Request #33935 · angular/angular-cli" github.com/angular/angula… #laco_feed