diff --git a/benchmark/child_process/child-process-exec-maxbuffer.js b/benchmark/child_process/child-process-exec-maxbuffer.js new file mode 100644 index 00000000000000..b41b8a541c11b5 --- /dev/null +++ b/benchmark/child_process/child-process-exec-maxbuffer.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common.js'); +const { execFile } = require('child_process'); + +// Isolates stdout accumulation + maxBuffer handling in execFile(). The child +// writes `chunks` blocks of 64 KiB; the parent accumulates them through the +// native pipe read path and the JS buffering in lib/child_process.js until the +// process exits and the result buffer is handed to the callback. + +const bench = common.createBenchmark(main, { + // Number of 64 KiB blocks written by the child: 1 MiB, 16 MiB, 64 MiB. + chunks: [16, 256, 1024], + n: [10], +}); + +function main({ n, chunks }) { + const script = + 'const b = Buffer.alloc(65536, 0x61);' + + `for (let i = 0; i < ${chunks}; i++) process.stdout.write(b);`; + const args = ['-e', script]; + const options = { + maxBuffer: chunks * 65536 + 65536, + encoding: 'buffer', + }; + + let left = n; + const run = () => { + execFile(process.execPath, args, options, (err) => { + if (err) + throw err; + if (--left === 0) + return bench.end(n); + run(); + }); + }; + + bench.start(); + run(); +} diff --git a/benchmark/child_process/child-process-ipc-roundtrip.js b/benchmark/child_process/child-process-ipc-roundtrip.js new file mode 100644 index 00000000000000..fe1817aa8ffb9a --- /dev/null +++ b/benchmark/child_process/child-process-ipc-roundtrip.js @@ -0,0 +1,47 @@ +'use strict'; +if (process.argv[2] === 'child') { + // Echo every message straight back to the parent. + process.on('message', (msg) => { + process.send(msg); + }); +} else { + const common = require('../common.js'); + const bench = common.createBenchmark(main, { + len: [64, 256, 1024, 4096, 16384, 65536], + serialization: ['json', 'advanced'], + dur: [5], + }); + const { spawn } = require('child_process'); + + function main({ dur, len, serialization }) { + const msg = { payload: '.'.repeat(len) }; + const options = { + stdio: ['ignore', 'ignore', 'ignore', 'ipc'], + serialization, + }; + const child = spawn(process.argv[0], + [process.argv[1], 'child'], options); + + let messages = 0; + let finished = false; + + child.on('message', () => { + messages++; + // Keep one round-trip in flight per completed one so both the serialize + // (write) and deserialize (read) paths stay saturated on both ends. + if (!finished) + child.send(msg); + }); + + bench.start(); + // Prime a window of in-flight messages so the IPC channel never drains. + for (let i = 0; i < 256; i++) + child.send(msg); + + setTimeout(() => { + finished = true; + bench.end(messages); + child.kill(); + }, dur * 1000); + } +} diff --git a/benchmark/child_process/child-process-spawn-options.js b/benchmark/child_process/child-process-spawn-options.js new file mode 100644 index 00000000000000..788faff8909d98 --- /dev/null +++ b/benchmark/child_process/child-process-spawn-options.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common.js'); +const { spawn } = require('child_process'); + +// Isolates the cost of marshaling spawn() options across the JS -> C++ boundary +// (ProcessWrap::Spawn). A trivial, fast-exiting child is spawned repeatedly +// while scaling the number of environment pairs and arguments that have to be +// converted, so the per-spawn option-handling overhead is the dominant cost. + +const isWindows = process.platform === 'win32'; +const command = isWindows ? 'cmd' : 'true'; +const baseArgs = isWindows ? ['/d', '/s', '/c', 'exit'] : []; + +const bench = common.createBenchmark(main, { + n: [1000], + envc: [0, 64, 256, 1024], + argc: [0, 8, 64], +}); + +function main({ n, envc, argc }) { + const env = { ...process.env }; + for (let i = 0; i < envc; i++) + env[`NODE_BENCH_VAR_${i}`] = `value_${i}`; + + const args = baseArgs.slice(); + for (let i = 0; i < argc; i++) + args.push(`arg_${i}`); + + const options = { env, stdio: ['ignore', 'ignore', 'ignore'] }; + + let left = n; + const go = () => { + if (--left < 0) + return bench.end(n); + const child = spawn(command, args, options); + // The exit code is intentionally ignored: the child only exercises the + // option-marshaling path, it is not expected to do any useful work. + child.on('exit', go); + }; + + bench.start(); + go(); +}