Files

192 lines
4.3 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable no-async-promise-executor */
/**
* To start the test, build the repo and run:
* node --expose-gc test.js empty 100000
* node --expose-gc test.js loadTest 10000
*/
'use strict';
const assert = require('node:assert');
const {performance} = require('node:perf_hooks');
const workerFarm = require('worker-farm');
const JestWorker = require('../').Worker;
assert(process.argv[2], 'Pass a child method name');
assert(process.argv[3], 'Pass the number of iterations');
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const method = process.argv[2];
const calls = Number(process.argv[3]);
const threads = 6;
const iterations = 10;
function testWorkerFarm() {
return new Promise(async resolve => {
const startTime = performance.now();
let count = 0;
async function countToFinish() {
if (++count === calls) {
workerFarm.end(api);
const endTime = performance.now();
// Let all workers go down.
await sleep(2000);
resolve({
globalTime: endTime - startTime - 2000,
processingTime: endTime - startProcess,
});
}
}
const api = workerFarm(
{
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: threads,
},
require.resolve('./workers/worker_farm'),
[method],
);
// Let all workers come up.
await sleep(2000);
const startProcess = performance.now();
for (let i = 0; i < calls; i++) {
const promisified = new Promise((resolve, reject) => {
api[method]((err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
promisified.then(countToFinish);
}
});
}
function testJestWorker() {
return new Promise(async resolve => {
const startTime = performance.now();
let count = 0;
async function countToFinish() {
if (++count === calls) {
const endTime = performance.now();
// Let all workers go down.
await farm.end();
resolve({
globalTime: endTime - startTime - 2000,
processingTime: endTime - startProcess,
});
}
}
const farm = new JestWorker(require.resolve('./workers/jest_worker'), {
exposedMethods: [method],
forkOptions: {execArgv: []},
numWorkers: threads,
});
farm.getStdout().pipe(process.stdout);
farm.getStderr().pipe(process.stderr);
// Let all workers come up.
await farm.start();
const startProcess = performance.now();
for (let i = 0; i < calls; i++) {
const promisified = farm[method]();
promisified.then(countToFinish);
}
});
}
function profile(x) {
console.profile(x);
}
function profileEnd(x) {
console.profileEnd(x);
}
async function main() {
if (!globalThis.gc) {
throw new Error('GC not present, start with node --expose-gc');
}
const wFResults = [];
const jWResults = [];
for (let i = 0; i < iterations; i++) {
console.log('-'.repeat(75));
profile('worker farm');
const wF = await testWorkerFarm();
profileEnd('worker farm');
await sleep(3000);
globalThis.gc?.();
profile('jest worker');
const jW = await testJestWorker();
profileEnd('jest worker');
await sleep(3000);
globalThis.gc?.();
wFResults.push(wF);
jWResults.push(jW);
console.log('jest-worker:', jW);
console.log('worker-farm:', wF);
}
let wFGT = 0;
let wFPT = 0;
let jWGT = 0;
let jWPT = 0;
for (let i = 0; i < iterations; i++) {
wFGT += wFResults[i].globalTime;
wFPT += wFResults[i].processingTime;
jWGT += jWResults[i].globalTime;
jWPT += jWResults[i].processingTime;
}
console.log('-'.repeat(75));
console.log('total worker-farm:', {wFGT, wFPT});
console.log('total jest-worker:', {jWGT, jWPT});
console.log('-'.repeat(75));
console.log(
`% improvement over ${calls} calls (global time):`,
(100 * (wFGT - jWGT)) / wFGT,
);
console.log(
`% improvement over ${calls} calls (processing time):`,
(100 * (wFPT - jWPT)) / wFPT,
);
}
main();