Repository navigation
[22.2.x]perf(@angular/build): process batch locales sequentially in i18n inliner worker - #34212
Conversation
…ner worker Executing batch locales concurrently via Promise.all within a single worker thread provided no concurrency benefit because inlining operations (MagicString transformations and source map decoding/remapping) are predominantly synchronous and CPU-bound on Node's single-threaded event loop. Holding concurrent MagicString instances, intermediate AST slices, and decoded source map mappings simultaneously inflated peak worker heap and RSS (especially for smaller chunks where up to 8 locales are processed in a single batch). Switching to sequential iteration allows intermediate transformation and mapping structures to be garbage-collected between locales, reducing peak RSS and slightly improving throughput due to reduced GC pressure.
… files in i18n inliner Dominant files (such as main.js or other large bundles >= 70% of max file size) previously sharded their locales across all available workers in the pool. On high-core machines, this dispatched heavy AST parsing and sourcemap remapping simultaneously across all worker threads, monopolizing the pool, causing memory bus/cache contention, and spiking peak V8 heap and RSS. A MAX_DOMINANT_WORKERS constant of 4 now caps the number of worker threads concurrently processing a single dominant file. Additionally, when multiple files exist in the window, workers are dynamically allocated to guarantee that at least one worker thread is always available to process smaller chunks concurrently without starvation. A new multi-dominant scenario has also been added to the benchmark suite to measure throughput and memory under multi-bundle workloads.
There was a problem hiding this comment.
Code Review
This pull request optimizes memory usage and concurrency in the i18n inliner. It replaces parallel processing of translation locales with a sequential loop in the worker to reduce peak memory. Additionally, it introduces a concurrency cap (MAX_DOMINANT_WORKERS = 4) when processing large 'dominant' files to prevent V8 heap and RSS explosion on high-core machines, while ensuring at least one worker remains available for smaller chunks to avoid starvation. A new benchmark scenario, 'multi-dominant', is also added to test these changes under contention. There are no review comments, and we have no additional feedback to provide.
|
This PR was merged into the repository. The changes were merged into the following branches:
|
Executing batch locales concurrently via Promise.all within a single
worker thread provided no concurrency benefit because inlining operations
(MagicString transformations and source map decoding/remapping) are
predominantly synchronous and CPU-bound on Node's single-threaded event loop.
Holding concurrent MagicString instances, intermediate AST slices, and
decoded source map mappings simultaneously inflated peak worker heap and RSS
(especially for smaller chunks where up to 8 locales are processed in a single
batch).
Switching to sequential iteration allows intermediate transformation and
mapping structures to be garbage-collected between locales, reducing peak RSS
and slightly improving throughput due to reduced GC pressure.