Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions packages/angular/build/src/tools/i18n/i18n-inliner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ const SMALL_FILE_FLOOR_BYTES = 100 * 1024;

/**
* Ratio of the maximum file size in a window to consider a file "dominant".
* Files within 70% of the largest file are sharded across all workers for maximum concurrency.
* Files within 70% of the largest file are sharded across bounded workers for concurrency.
*/
const DOMINANT_FILE_RATIO = 0.7;

/**
* Maximum number of worker threads to allocate concurrently for a single dominant file.
* Maximum number of worker threads to allocate concurrently across dominant files.
* Capping concurrency on dominant files (which hold large AST and sourcemap trees)
* prevents V8 heap and RSS explosion on machines with high CPU core counts.
*/
Expand Down Expand Up @@ -520,6 +520,20 @@ export class I18nInliner {
// while small files act as gap fillers near the window barrier to prevent tail stragglers.
sortedFiles.sort((a, b) => b.fileSize - a.fileSize);

const dominantThreshold = Math.max(SMALL_FILE_FLOOR_BYTES, maxFileSize * DOMINANT_FILE_RATIO);
let dominantCount = 0;
for (const { fileSize } of sortedFiles) {
if (fileSize >= dominantThreshold) {
dominantCount++;
} else {
break;
}
}
const dominantTargetWorkers =
dominantCount > 0
? Math.max(1, Math.min(workerCount - 1, Math.floor(MAX_DOMINANT_WORKERS / dominantCount)))
: 1;

const workerTasks: Promise<void>[] = [];

for (const { filename, entries, codeFile, fileSize } of sortedFiles) {
Expand All @@ -535,13 +549,16 @@ export class I18nInliner {
} else if (fileSize < SMALL_FILE_FLOOR_BYTES) {
// Small chunks (< 100 KB): process all locales in 1 batch to eliminate IPC overhead
localesPerBatch = entries.length;
} else if (fileSize >= maxFileSize * DOMINANT_FILE_RATIO) {
} else if (fileSize >= dominantThreshold) {
// Dominant file(s) with multiple files: shard across bounded workers while always reserving
// at least one worker to process smaller chunks in parallel without starvation.
const targetWorkers = Math.max(1, Math.min(workerCount - 1, MAX_DOMINANT_WORKERS));
localesPerBatch = Math.max(1, Math.ceil(entries.length / targetWorkers));
localesPerBatch = Math.max(1, Math.ceil(entries.length / dominantTargetWorkers));
} else if (uncachedByFile.size >= workerCount) {
// Worker pool is already saturated by file-level concurrency;
// process all locales in a single batch to avoid duplicate parsing and IPC overhead.
localesPerBatch = entries.length;
} else {
// Intermediate files: moderate sharding
// Intermediate files with idle workers: moderate sharding
localesPerBatch = Math.max(1, Math.ceil(entries.length / 2));
}

Expand Down
Loading