Skip to content

Commit f0fda8b

Browse files
committed
benchmark: switch communication with spawned process to 'pipe'
Whole benchmark script is synchronous anyway because we can't measure stuff in parallel but were dealing async/await/Promise just because we used IPC to communicate with spawned subprocess. Now we switch to 'pipe' + JSON.stringify/JSON.parse for comminication and can simply source code a lot.
1 parent b42eceb commit f0fda8b

File tree

2 files changed

+41
-52
lines changed

2 files changed

+41
-52
lines changed

.eslintrc.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,6 @@ overrides:
708708
import/no-extraneous-dependencies: [error, { devDependencies: true }]
709709
import/no-nodejs-modules: off
710710
no-console: off
711-
no-await-in-loop: off
712711
- files: 'resources/eslint-internal-rules/**'
713712
env:
714713
node: true

resources/benchmark.ts

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ const maxTime = 5;
1212
// The minimum sample size required to perform statistical analysis.
1313
const minSamples = 5;
1414

15-
async function runBenchmarks() {
15+
function runBenchmarks() {
1616
// Get the revisions and make things happen!
1717
const { benchmarks, revisions } = getArguments(process.argv.slice(2));
1818
const benchmarkProjects = prepareBenchmarkProjects(revisions);
1919

2020
for (const benchmark of benchmarks) {
21-
await runBenchmark(benchmark, benchmarkProjects);
21+
runBenchmark(benchmark, benchmarkProjects);
2222
}
2323
}
2424

@@ -113,14 +113,14 @@ function prepareBenchmarkProjects(
113113
}
114114
}
115115

116-
async function collectSamples(modulePath: string) {
116+
function collectSamples(modulePath: string) {
117117
let numOfConsequentlyRejectedSamples = 0;
118118
const samples = [];
119119

120120
// If time permits, increase sample size to reduce the margin of error.
121121
const start = Date.now();
122122
while (samples.length < minSamples || (Date.now() - start) / 1e3 < maxTime) {
123-
const sample = await sampleModule(modulePath);
123+
const sample = sampleModule(modulePath);
124124

125125
if (sample.involuntaryContextSwitches > 0) {
126126
numOfConsequentlyRejectedSamples++;
@@ -278,7 +278,7 @@ function maxBy<T>(array: ReadonlyArray<T>, fn: (obj: T) => number) {
278278
}
279279

280280
// Prepare all revisions and run benchmarks matching a pattern against them.
281-
async function runBenchmark(
281+
function runBenchmark(
282282
benchmark: string,
283283
benchmarkProjects: ReadonlyArray<BenchmarkProject>,
284284
) {
@@ -288,17 +288,17 @@ async function runBenchmark(
288288
const modulePath = path.join(projectPath, benchmark);
289289

290290
if (i === 0) {
291-
const { name } = await sampleModule(modulePath);
291+
const { name } = sampleModule(modulePath);
292292
console.log('⏱ ' + name);
293293
}
294294

295295
try {
296-
const samples = await collectSamples(modulePath);
296+
const samples = collectSamples(modulePath);
297297

298298
results.push(computeStats(revision, samples));
299299
process.stdout.write(' ' + cyan(i + 1) + ' tests completed.\u000D');
300300
} catch (error) {
301-
console.log(' ' + revision + ': ' + red(String(error)));
301+
console.log(' ' + revision + ': ' + red(error.message));
302302
}
303303
}
304304
console.log('\n');
@@ -372,11 +372,9 @@ interface BenchmarkSample {
372372
involuntaryContextSwitches: number;
373373
}
374374

375-
function sampleModule(modulePath: string): Promise<BenchmarkSample> {
375+
function sampleModule(modulePath: string): BenchmarkSample {
376376
const sampleCode = `
377-
import assert from 'node:assert';
378-
379-
assert(process.send);
377+
import fs from 'node:fs';
380378
381379
import { benchmark } from '${modulePath}';
382380
@@ -399,53 +397,45 @@ function sampleModule(modulePath: string): Promise<BenchmarkSample> {
399397
const timeDiff = Number(process.hrtime.bigint() - startTime);
400398
const resourcesEnd = process.resourceUsage();
401399
402-
process.send({
400+
const result = {
403401
name: benchmark.name,
404402
clocked: timeDiff / benchmark.count,
405403
memUsed: (process.memoryUsage().heapUsed - memBaseline) / benchmark.count,
406404
involuntaryContextSwitches:
407405
resourcesEnd.involuntaryContextSwitches - resourcesStart.involuntaryContextSwitches,
408-
});
406+
};
407+
fs.writeFileSync(3, JSON.stringify(result));
409408
`;
410409

411-
return new Promise((resolve, reject) => {
412-
const child = cp.spawn(
413-
process.execPath,
414-
[
415-
// V8 flags
416-
'--predictable',
417-
'--no-concurrent-sweeping',
418-
'--no-scavenge-task',
419-
'--min-semi-space-size=1024', // 1GB
420-
'--max-semi-space-size=1024', // 1GB
421-
'--trace-gc', // no gc calls should happen during benchmark, so trace them
422-
423-
// Node.js flags
424-
'--input-type=module',
425-
'--eval',
426-
sampleCode,
427-
],
428-
{
429-
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
430-
env: { NODE_ENV: 'production' },
431-
},
432-
);
410+
const result = cp.spawnSync(
411+
process.execPath,
412+
[
413+
// V8 flags
414+
'--predictable',
415+
'--no-concurrent-sweeping',
416+
'--no-scavenge-task',
417+
'--min-semi-space-size=1024', // 1GB
418+
'--max-semi-space-size=1024', // 1GB
419+
'--trace-gc', // no gc calls should happen during benchmark, so trace them
420+
421+
// Node.js flags
422+
'--input-type=module',
423+
'--eval',
424+
sampleCode,
425+
],
426+
{
427+
stdio: ['inherit', 'inherit', 'inherit', 'pipe'],
428+
env: { NODE_ENV: 'production' },
429+
},
430+
);
433431

434-
let message: any;
435-
let error: any;
432+
if (result.status !== 0) {
433+
throw new Error(`Benchmark failed with "${result.status}" status.`);
434+
}
436435

437-
child.on('message', (msg) => (message = msg));
438-
child.on('error', (e) => (error = e));
439-
child.on('close', () => {
440-
if (message) {
441-
return resolve(message);
442-
}
443-
reject(error || new Error('Spawn process closed without error'));
444-
});
445-
});
436+
const resultStr = result.output[3]?.toString();
437+
assert(resultStr != null);
438+
return JSON.parse(resultStr);
446439
}
447440

448-
runBenchmarks().catch((error) => {
449-
console.error(error);
450-
process.exit(1);
451-
});
441+
runBenchmarks();

0 commit comments

Comments
 (0)