diff --git a/benchmark/repl/completion.js b/benchmark/repl/completion.js new file mode 100644 index 00000000000000..b9f55d8416b0f7 --- /dev/null +++ b/benchmark/repl/completion.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common.js'); +const repl = require('node:repl'); +const { PassThrough, Writable } = require('node:stream'); + +const bench = common.createBenchmark(main, { + n: [5e3], + query: [ + 'cons', + 'console.lo', + 'Buffer.prototype.wri', + "require('f", + ], + useGlobal: [0, 1], +}); + +function main({ n, query, useGlobal }) { + const input = new PassThrough(); + const output = new Writable({ write(c, e, cb) { cb(); } }); + const server = new repl.REPLServer({ + input, + output, + terminal: false, + useGlobal: !!useGlobal, + }); + + // Inspector callbacks do not keep the event loop alive on their own. + const keepAlive = setInterval(() => {}, 0x7fffffff); + let remaining = n; + + function complete() { + server.complete(query, onComplete); + } + + function onComplete(err) { + if (err) { + throw err; + } + + if (--remaining === 0) { + bench.end(n); + clearInterval(keepAlive); + server.close(); + return; + } + + setImmediate(complete); + } + + bench.start(); + setImmediate(complete); +} diff --git a/benchmark/repl/creation.js b/benchmark/repl/creation.js new file mode 100644 index 00000000000000..60e795063d1922 --- /dev/null +++ b/benchmark/repl/creation.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common.js'); +const repl = require('node:repl'); +const { PassThrough, Writable } = require('node:stream'); + +const bench = common.createBenchmark(main, { + n: [500], + preview: [0, 1], + terminal: [0, 1], + useGlobal: [0, 1], +}, { + combinationFilter: ({ preview, terminal }) => !!terminal || !preview, +}); + +function main({ n, preview, terminal, useGlobal }) { + const inputs = Array.from({ length: n }, () => new PassThrough()); + const outputs = Array.from( + { length: n }, + () => new Writable({ write(c, e, cb) { cb(); } }), + ); + const servers = new Array(n); + + bench.start(); + for (let i = 0; i < n; i++) { + servers[i] = new repl.REPLServer({ + input: inputs[i], + output: outputs[i], + preview: !!preview, + terminal: !!terminal, + useGlobal: !!useGlobal, + }); + } + bench.end(n); + + for (const server of servers) { + server.close(); + } +} diff --git a/benchmark/repl/evaluate.js b/benchmark/repl/evaluate.js new file mode 100644 index 00000000000000..48376a2b48fe55 --- /dev/null +++ b/benchmark/repl/evaluate.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common.js'); +const repl = require('node:repl'); +const { PassThrough, Writable } = require('node:stream'); + +const bench = common.createBenchmark(main, { + n: [2e4], + code: [ + '1 + 1', + '({ answer: 42 })', + 'Promise.resolve(42)', + 'await Promise.resolve(42)', + ], + mode: ['sloppy', 'strict'], + useGlobal: [0, 1], +}); + +function main({ n, code, mode, useGlobal }) { + const input = new PassThrough(); + const output = new Writable({ write(c, e, cb) { cb(); } }); + const server = new repl.REPLServer({ + input, + output, + replMode: mode === 'strict' ? + repl.REPL_MODE_STRICT : + repl.REPL_MODE_SLOPPY, + terminal: false, + useGlobal: !!useGlobal, + }); + + // Inspector callbacks do not keep the event loop alive on their own. + const keepAlive = setInterval(() => {}, 0x7fffffff); + let remaining = n; + + function evaluate() { + server.eval(`${code}\n`, server.context, 'repl', onEvaluate); + } + + function onEvaluate(err) { + if (err) { + throw err; + } + + if (--remaining === 0) { + bench.end(n); + clearInterval(keepAlive); + server.close(); + return; + } + + setImmediate(evaluate); + } + + bench.start(); + setImmediate(evaluate); +} diff --git a/benchmark/repl/process-lines.js b/benchmark/repl/process-lines.js new file mode 100644 index 00000000000000..fd019512f9fc32 --- /dev/null +++ b/benchmark/repl/process-lines.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common.js'); +const repl = require('node:repl'); +const { PassThrough, Writable } = require('node:stream'); + +const bench = common.createBenchmark(main, { + n: [1e4], + code: [ + '1 + 1\n', + 'Promise.resolve(42)\n', + ], + mode: ['sloppy', 'strict'], + terminal: [0, 1], + useGlobal: [0, 1], +}); + +function main({ n, code: inputCode, mode, terminal, useGlobal }) { + const input = new PassThrough(); + const output = new Writable({ write(c, e, cb) { cb(); } }); + const server = new repl.REPLServer({ + input, + output, + replMode: mode === 'strict' ? + repl.REPL_MODE_STRICT : + repl.REPL_MODE_SLOPPY, + terminal: !!terminal, + useGlobal: !!useGlobal, + }); + const originalEval = server.eval; + // TTY input dispatch can briefly have no other active event loop handles. + const keepAlive = setInterval(() => {}, 0x7fffffff); + let remaining = n; + + // eslint-disable-next-line node-core/func-name-matching + server.eval = function REPLEval(code, context, file, callback) { + originalEval(code, context, file, function onEvaluate() { + const result = Reflect.apply(callback, this, arguments); + if (--remaining === 0) { + bench.end(n); + clearInterval(keepAlive); + server.close(); + } else { + setImmediate(() => input.write(inputCode)); + } + return result; + }); + }; + + bench.start(); + input.write(inputCode); +} diff --git a/benchmark/repl/reset-context.js b/benchmark/repl/reset-context.js new file mode 100644 index 00000000000000..ab96f92564f298 --- /dev/null +++ b/benchmark/repl/reset-context.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common.js'); +const repl = require('node:repl'); +const { PassThrough, Writable } = require('node:stream'); + +const bench = common.createBenchmark(main, { + n: [1e3], +}); + +function main({ n }) { + const input = new PassThrough(); + const output = new Writable({ write(c, e, cb) { cb(); } }); + const server = new repl.REPLServer({ + input, + output, + terminal: false, + }); + + bench.start(); + for (let i = 0; i < n; i++) { + server.resetContext(); + } + bench.end(n); + server.close(); +} diff --git a/test/benchmark/test-benchmark-repl.js b/test/benchmark/test-benchmark-repl.js new file mode 100644 index 00000000000000..2956bbd63dfee4 --- /dev/null +++ b/test/benchmark/test-benchmark-repl.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); + +const runBenchmark = require('../common/benchmark'); + +runBenchmark('repl', { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 });