
`ngcc` supports both synchronous and asynchronous execution. The default mode when using `ngcc` programmatically (which is how `@angular/cli` is using it) is synchronous. When running `ngcc` from the command line (i.e. via the `ivy-ngcc` script), it runs in async mode. Previously, the work would be executed in the same way in both modes. This commit improves the performance of `ngcc` in async mode by processing tasks in parallel on multiple processes. It uses the Node.js built-in [`cluster` module](https://nodejs.org/api/cluster.html) to launch a cluster of Node.js processes and take advantage of multi-core systems. Preliminary comparisons indicate a 1.8x to 2.6x speed improvement when processing the angular.io app (apparently depending on the OS, number of available cores, system load, etc.). Further investigation is needed to better understand these numbers and identify potential areas of improvement. Inspired by/Based on @alxhub's prototype: alxhub/angular@cb631bdb1 Original design doc: https://hackmd.io/uYG9CJrFQZ-6FtKqpnYJAA?view Jira issue: [FW-1460](https://angular-team.atlassian.net/browse/FW-1460) PR Close #32427
82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/// <reference types="node" />
|
|
|
|
import * as cluster from 'cluster';
|
|
|
|
import {MessageFromWorker, MessageToWorker} from './api';
|
|
|
|
|
|
|
|
/** Expose a `Promise` instance as well as APIs for resolving/rejecting it. */
|
|
export class Deferred<T> {
|
|
/**
|
|
* Resolve the associated promise with the specified value.
|
|
* If the value is a rejection (constructed with `Promise.reject()`), the promise will be rejected
|
|
* instead.
|
|
*
|
|
* @param value The value to resolve the promise with.
|
|
*/
|
|
resolve !: (value: T) => void;
|
|
|
|
/**
|
|
* Rejects the associated promise with the specified reason.
|
|
*
|
|
* @param reason The rejection reason.
|
|
*/
|
|
reject !: (reason: any) => void;
|
|
|
|
/** The `Promise` instance associated with this deferred. */
|
|
promise = new Promise<T>((resolve, reject) => {
|
|
this.resolve = resolve;
|
|
this.reject = reject;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send a message to the cluster master.
|
|
* (This function should be invoked from cluster workers only.)
|
|
*
|
|
* @param msg The message to send to the cluster master.
|
|
*/
|
|
export const sendMessageToMaster = (msg: MessageFromWorker): void => {
|
|
if (cluster.isMaster) {
|
|
throw new Error('Unable to send message to the master process: Already on the master process.');
|
|
}
|
|
|
|
if (process.send === undefined) {
|
|
// Theoretically, this should never happen on a worker process.
|
|
throw new Error('Unable to send message to the master process: Missing `process.send()`.');
|
|
}
|
|
|
|
process.send(msg);
|
|
};
|
|
|
|
/**
|
|
* Send a message to a cluster worker.
|
|
* (This function should be invoked from the cluster master only.)
|
|
*
|
|
* @param workerId The ID of the recipient worker.
|
|
* @param msg The message to send to the worker.
|
|
*/
|
|
export const sendMessageToWorker = (workerId: number, msg: MessageToWorker): void => {
|
|
if (!cluster.isMaster) {
|
|
throw new Error('Unable to send message to worker process: Sender is not the master process.');
|
|
}
|
|
|
|
const worker = cluster.workers[workerId];
|
|
|
|
if ((worker === undefined) || worker.isDead() || !worker.isConnected()) {
|
|
throw new Error(
|
|
'Unable to send message to worker process: Recipient does not exist or has disconnected.');
|
|
}
|
|
|
|
worker.send(msg);
|
|
};
|