diff --git a/benchmark/_http-benchmarkers.js b/benchmark/_http-benchmarkers.js index ad586f1cb60ef6..78e4248bfab46e 100644 --- a/benchmark/_http-benchmarkers.js +++ b/benchmark/_http-benchmarkers.js @@ -1,74 +1,119 @@ 'use strict'; const child_process = require('child_process'); +const path = require('path'); +const fs = require('fs'); // The port used by servers and wrk exports.PORT = process.env.PORT || 12346; -function AutocannonBenchmarker() { - this.name = 'autocannon'; - this.autocannon_exe = process.platform === 'win32' ? - 'autocannon.cmd' : - 'autocannon'; - const result = child_process.spawnSync(this.autocannon_exe, ['-h']); - this.present = !(result.error && result.error.code === 'ENOENT'); -} +class AutocannonBenchmarker { + constructor() { + this.name = 'autocannon'; + this.executable = process.platform === 'win32' ? + 'autocannon.cmd' : + 'autocannon'; + const result = child_process.spawnSync(this.executable, ['-h']); + this.present = !(result.error && result.error.code === 'ENOENT'); + } -AutocannonBenchmarker.prototype.create = function(options) { - const args = [ - '-d', options.duration, - '-c', options.connections, - '-j', - '-n', - `http://127.0.0.1:${options.port}${options.path}` - ]; - const child = child_process.spawn(this.autocannon_exe, args); - return child; -}; + create(options) { + const args = [ + '-d', options.duration, + '-c', options.connections, + '-j', + '-n', + `http://127.0.0.1:${options.port}${options.path}` + ]; + const child = child_process.spawn(this.executable, args); + return child; + } -AutocannonBenchmarker.prototype.processResults = function(output) { - let result; - try { - result = JSON.parse(output); - } catch (err) { - // Do nothing, let next line handle this + processResults(output) { + let result; + try { + result = JSON.parse(output); + } catch (err) { + return undefined; + } + if (!result || !result.requests || !result.requests.average) { + return undefined; + } else { + return result.requests.average; + } } - if (!result || !result.requests || !result.requests.average) { - return undefined; - } else { - return result.requests.average; +} + +class WrkBenchmarker { + constructor() { + this.name = 'wrk'; + this.executable = 'wrk'; + const result = child_process.spawnSync(this.executable, ['-h']); + this.present = !(result.error && result.error.code === 'ENOENT'); + } + + create(options) { + const args = [ + '-d', options.duration, + '-c', options.connections, + '-t', 8, + `http://127.0.0.1:${options.port}${options.path}` + ]; + const child = child_process.spawn(this.executable, args); + return child; } -}; -function WrkBenchmarker() { - this.name = 'wrk'; - this.regexp = /Requests\/sec:[ \t]+([0-9.]+)/; - const result = child_process.spawnSync('wrk', ['-h']); - this.present = !(result.error && result.error.code === 'ENOENT'); + processResults(output) { + const throughputRe = /Requests\/sec:[ \t]+([0-9.]+)/; + const match = output.match(throughputRe); + const throughput = match && +match[1]; + if (!isFinite(throughput)) { + return undefined; + } else { + return throughput; + } + } } -WrkBenchmarker.prototype.create = function(options) { - const args = [ - '-d', options.duration, - '-c', options.connections, - '-t', 8, - `http://127.0.0.1:${options.port}${options.path}` - ]; - const child = child_process.spawn('wrk', args); - return child; -}; +/** + * Simple, single-threaded benchmarker for testing if the benchmark + * works + */ +class TestDoubleBenchmarker { + constructor() { + this.name = 'test-double'; + this.executable = path.resolve(__dirname, '_test-double-benchmarker.js'); + this.present = fs.existsSync(this.executable); + } + + create(options) { + const child = child_process.fork(this.executable, { + silent: true, + env: { + duration: options.duration, + connections: options.connections, + path: `http://127.0.0.1:${options.port}${options.path}` + } + }); + return child; + } -WrkBenchmarker.prototype.processResults = function(output) { - const match = output.match(this.regexp); - const result = match && +match[1]; - if (!isFinite(result)) { - return undefined; - } else { - return result; + processResults(output) { + let result; + try { + result = JSON.parse(output); + } catch (err) { + return undefined; + } + return result.throughput; } -}; +} -const http_benchmarkers = [new WrkBenchmarker(), new AutocannonBenchmarker()]; +const http_benchmarkers = [ + new WrkBenchmarker(), + new AutocannonBenchmarker(), + new TestDoubleBenchmarker() +]; const benchmarkers = {}; diff --git a/benchmark/_test-double-benchmarker.js b/benchmark/_test-double-benchmarker.js new file mode 100644 index 00000000000000..a95ad9c8613db8 --- /dev/null +++ b/benchmark/_test-double-benchmarker.js @@ -0,0 +1,7 @@ +'use strict'; + +const http = require('http'); + +http.get(process.env.path, function() { + console.log(JSON.stringify({throughput: 1})); +}); diff --git a/benchmark/http/_http_simple.js b/benchmark/fixtures/simple-http-server.js similarity index 93% rename from benchmark/http/_http_simple.js rename to benchmark/fixtures/simple-http-server.js index 6886ccaf64d996..1dda98e9f2d9c1 100644 --- a/benchmark/http/_http_simple.js +++ b/benchmark/fixtures/simple-http-server.js @@ -2,8 +2,6 @@ var http = require('http'); -var port = parseInt(process.env.PORT || 8000); - var fixed = 'C'.repeat(20 * 1024); var storedBytes = Object.create(null); var storedBuffer = Object.create(null); @@ -22,7 +20,7 @@ if (useDomains) { gdom.enter(); } -var server = module.exports = http.createServer(function(req, res) { +module.exports = http.createServer(function(req, res) { if (useDomains) { var dom = domain.create(); dom.add(req); @@ -142,8 +140,3 @@ var server = module.exports = http.createServer(function(req, res) { res.end(body); } }); - -server.listen(port, function() { - if (module === require.main) - console.error('Listening at http://127.0.0.1:' + port + '/'); -}); diff --git a/benchmark/http/_chunky_http_client.js b/benchmark/http/_chunky_http_client.js index d4d60ac84d1796..3dbf2caecefcd4 100644 --- a/benchmark/http/_chunky_http_client.js +++ b/benchmark/http/_chunky_http_client.js @@ -7,14 +7,14 @@ var test = require('../../test/common.js'); var bench = common.createBenchmark(main, { len: [1, 4, 8, 16, 32, 64, 128], - num: [5, 50, 500, 2000], + n: [5, 50, 500, 2000], type: ['send'], }); function main(conf) { var len = +conf.len; - var num = +conf.num; + var num = +conf.n; var todo = []; var headers = []; // Chose 7 because 9 showed "Connection error" / "Connection closed" diff --git a/benchmark/http/bench-parser.js b/benchmark/http/bench-parser.js index 0a78f3ffcf2074..1bc661e7289168 100644 --- a/benchmark/http/bench-parser.js +++ b/benchmark/http/bench-parser.js @@ -10,17 +10,17 @@ const kOnMessageComplete = HTTPParser.kOnMessageComplete | 0; const CRLF = '\r\n'; const bench = common.createBenchmark(main, { - fields: [4, 8, 16, 32], + len: [4, 8, 16, 32], n: [1e5], }); function main(conf) { - const fields = conf.fields >>> 0; + const len = conf.len >>> 0; const n = conf.n >>> 0; var header = `GET /hello HTTP/1.1${CRLF}Content-Type: text/plain${CRLF}`; - for (var i = 0; i < fields; i++) { + for (var i = 0; i < len; i++) { header += `X-Filler${i}: ${Math.random().toString(36).substr(2)}${CRLF}`; } header += CRLF; diff --git a/benchmark/http/chunked.js b/benchmark/http/chunked.js index 46d6ab2e266879..34a06a1a6d8320 100644 --- a/benchmark/http/chunked.js +++ b/benchmark/http/chunked.js @@ -11,14 +11,14 @@ var common = require('../common.js'); var bench = common.createBenchmark(main, { - num: [1, 4, 8, 16], - size: [1, 64, 256], + n: [1, 4, 8, 16], + len: [1, 64, 256], c: [100] }); function main(conf) { const http = require('http'); - var chunk = Buffer.alloc(conf.size, '8'); + var chunk = Buffer.alloc(conf.len, '8'); var server = http.createServer(function(req, res) { function send(left) { @@ -28,7 +28,7 @@ function main(conf) { send(left - 1); }, 0); } - send(conf.num); + send(conf.n); }); server.listen(common.PORT, function() { diff --git a/benchmark/http/client-request-body.js b/benchmark/http/client-request-body.js index ab7e3877f38ef2..d521c8a2c847e3 100644 --- a/benchmark/http/client-request-body.js +++ b/benchmark/http/client-request-body.js @@ -7,13 +7,13 @@ var http = require('http'); var bench = common.createBenchmark(main, { dur: [5], type: ['asc', 'utf', 'buf'], - bytes: [32, 256, 1024], + len: [32, 256, 1024], method: ['write', 'end'] }); function main(conf) { var dur = +conf.dur; - var len = +conf.bytes; + var len = +conf.len; var encoding; var chunk; diff --git a/benchmark/http/cluster.js b/benchmark/http/cluster.js index 732a5fad6646c9..464bcfdb6311e4 100644 --- a/benchmark/http/cluster.js +++ b/benchmark/http/cluster.js @@ -7,11 +7,12 @@ if (cluster.isMaster) { var bench = common.createBenchmark(main, { // unicode confuses ab on os x. type: ['bytes', 'buffer'], - length: [4, 1024, 102400], + len: [4, 1024, 102400], c: [50, 500] }); } else { - require('./_http_simple.js'); + var port = parseInt(process.env.PORT || PORT); + require('../fixtures/simple-http-server.js').listen(port); } function main(conf) { @@ -26,7 +27,7 @@ function main(conf) { return; setTimeout(function() { - var path = '/' + conf.type + '/' + conf.length; + var path = '/' + conf.type + '/' + conf.len; bench.http({ path: path, diff --git a/benchmark/http/create-clientrequest.js b/benchmark/http/create-clientrequest.js index 76134663d00a79..f40ff9155dae50 100644 --- a/benchmark/http/create-clientrequest.js +++ b/benchmark/http/create-clientrequest.js @@ -4,15 +4,15 @@ var common = require('../common.js'); var ClientRequest = require('http').ClientRequest; var bench = common.createBenchmark(main, { - pathlen: [1, 8, 16, 32, 64, 128], + len: [1, 8, 16, 32, 64, 128], n: [1e6] }); function main(conf) { - var pathlen = +conf.pathlen; + var len = +conf.len; var n = +conf.n; - var path = '/'.repeat(pathlen); + var path = '/'.repeat(len); var opts = { path: path, createConnection: function() {} }; bench.start(); diff --git a/benchmark/http/end-vs-write-end.js b/benchmark/http/end-vs-write-end.js index 3c216e766c53e8..163ad595a93aca 100644 --- a/benchmark/http/end-vs-write-end.js +++ b/benchmark/http/end-vs-write-end.js @@ -12,7 +12,7 @@ var common = require('../common.js'); var bench = common.createBenchmark(main, { type: ['asc', 'utf', 'buf'], - kb: [64, 128, 256, 1024], + len: [64 * 1024, 128 * 1024, 256 * 1024, 1024 * 1024], c: [100], method: ['write', 'end'] }); @@ -20,7 +20,7 @@ var bench = common.createBenchmark(main, { function main(conf) { const http = require('http'); var chunk; - var len = conf.kb * 1024; + var len = conf.len; switch (conf.type) { case 'buf': chunk = Buffer.alloc(len, 'x'); diff --git a/benchmark/http/simple.js b/benchmark/http/simple.js index 39c8f29dc89a74..c773e717913e99 100644 --- a/benchmark/http/simple.js +++ b/benchmark/http/simple.js @@ -5,7 +5,7 @@ var PORT = common.PORT; var bench = common.createBenchmark(main, { // unicode confuses ab on os x. type: ['bytes', 'buffer'], - length: [4, 1024, 102400], + len: [4, 1024, 102400], chunks: [0, 1, 4], // chunks=0 means 'no chunked encoding'. c: [50, 500], res: ['normal', 'setHeader', 'setHeaderWH'] @@ -13,9 +13,10 @@ var bench = common.createBenchmark(main, { function main(conf) { process.env.PORT = PORT; - var server = require('./_http_simple.js'); - setTimeout(function() { - var path = '/' + conf.type + '/' + conf.length + '/' + conf.chunks + '/' + + var server = require('../fixtures/simple-http-server.js') + .listen(process.env.PORT || common.PORT) + .on('listening', function() { + var path = '/' + conf.type + '/' + conf.len + '/' + conf.chunks + '/' + conf.res; bench.http({ @@ -24,5 +25,5 @@ function main(conf) { }, function() { server.close(); }); - }, 2000); + }); } diff --git a/test/sequential/test-benchmark-http.js b/test/sequential/test-benchmark-http.js new file mode 100644 index 00000000000000..fc9272779b5217 --- /dev/null +++ b/test/sequential/test-benchmark-http.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); + +if (!common.enoughTestMem) { + common.skip('Insufficient memory for HTTP benchmark test'); + return; +} + +// Minimal test for http benchmarks. This makes sure the benchmarks aren't +// horribly broken but nothing more than that. + +// Because the http benchmarks use hardcoded ports, this should be in sequential +// rather than parallel to make sure it does not conflict with tests that choose +// random available ports. + +const assert = require('assert'); +const fork = require('child_process').fork; +const path = require('path'); + +const runjs = path.join(__dirname, '..', '..', 'benchmark', 'run.js'); + +const child = fork(runjs, ['--set', 'dur=0.1', + '--set', 'n=1', + '--set', 'len=1', + '--set', 'c=1', + '--set', 'chunks=0', + '--set', 'benchmarker=test-double', + 'http'], + {env: {NODEJS_BENCHMARK_ZERO_ALLOWED: 1}}); +child.on('exit', (code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); +});