Skip to content

Commit 5d92cec

Browse files
BridgeARMylesBorins
authored andcommittedMar 9, 2020
benchmark: add test and all options and improve errors"
This reverts commit 4671d55 and contains a fix to the issue raised for the revert. PR-URL: #31755 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent e11f38c commit 5d92cec

38 files changed

+305
-200
lines changed
 

‎benchmark/_cli.js

+16-6
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,24 @@ const path = require('path');
66
// Create an object of all benchmark scripts
77
const benchmarks = {};
88
fs.readdirSync(__dirname)
9-
.filter((name) => fs.statSync(path.resolve(__dirname, name)).isDirectory())
9+
.filter((name) => {
10+
return name !== 'fixtures' &&
11+
fs.statSync(path.resolve(__dirname, name)).isDirectory();
12+
})
1013
.forEach((category) => {
1114
benchmarks[category] = fs.readdirSync(path.resolve(__dirname, category))
1215
.filter((filename) => filename[0] !== '.' && filename[0] !== '_');
1316
});
1417

1518
function CLI(usage, settings) {
16-
if (!(this instanceof CLI)) return new CLI(usage, settings);
17-
1819
if (process.argv.length < 3) {
1920
this.abort(usage); // Abort will exit the process
2021
}
2122

2223
this.usage = usage;
2324
this.optional = {};
2425
this.items = [];
26+
this.test = false;
2527

2628
for (const argName of settings.arrayArgs) {
2729
this.optional[argName] = [];
@@ -34,7 +36,7 @@ function CLI(usage, settings) {
3436
if (arg === '--') {
3537
// Only items can follow --
3638
mode = 'item';
37-
} else if ('both' === mode && arg[0] === '-') {
39+
} else if (mode === 'both' && arg[0] === '-') {
3840
// Optional arguments declaration
3941

4042
if (arg[1] === '-') {
@@ -61,6 +63,8 @@ function CLI(usage, settings) {
6163

6264
// The next value can be either an option or an item
6365
mode = 'both';
66+
} else if (arg === 'test') {
67+
this.test = true;
6468
} else if (['both', 'item'].includes(mode)) {
6569
// item arguments
6670
this.items.push(arg);
@@ -83,9 +87,15 @@ CLI.prototype.abort = function(msg) {
8387
CLI.prototype.benchmarks = function() {
8488
const paths = [];
8589

90+
if (this.items.includes('all')) {
91+
this.items = Object.keys(benchmarks);
92+
}
93+
8694
for (const category of this.items) {
87-
if (benchmarks[category] === undefined)
88-
continue;
95+
if (benchmarks[category] === undefined) {
96+
console.error(`The "${category}" category does not exist.`);
97+
process.exit(1);
98+
}
8999
for (const scripts of benchmarks[category]) {
90100
if (this.shouldSkip(scripts)) continue;
91101

‎benchmark/_http-benchmarkers.js

+19-13
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ class AutocannonBenchmarker {
4343
}
4444
if (!result || !result.requests || !result.requests.average) {
4545
return undefined;
46-
} else {
47-
return result.requests.average;
4846
}
47+
return result.requests.average;
4948
}
5049
}
5150

@@ -58,10 +57,13 @@ class WrkBenchmarker {
5857
}
5958

6059
create(options) {
60+
const duration = typeof options.duration === 'number' ?
61+
Math.max(options.duration, 1) :
62+
options.duration;
6163
const args = [
62-
'-d', options.duration,
64+
'-d', duration,
6365
'-c', options.connections,
64-
'-t', 8,
66+
'-t', Math.min(options.connections, require('os').cpus().length || 8),
6567
`http://127.0.0.1:${options.port}${options.path}`,
6668
];
6769
for (const field in options.headers) {
@@ -77,9 +79,8 @@ class WrkBenchmarker {
7779
const throughput = match && +match[1];
7880
if (!isFinite(throughput)) {
7981
return undefined;
80-
} else {
81-
return throughput;
8282
}
83+
return throughput;
8384
}
8485
}
8586

@@ -89,18 +90,21 @@ class WrkBenchmarker {
8990
*/
9091
class TestDoubleBenchmarker {
9192
constructor(type) {
92-
// `type` is the type ofbenchmarker. Possible values are 'http' and 'http2'.
93+
// `type` is the type of benchmarker. Possible values are 'http' and
94+
// 'http2'.
9395
this.name = `test-double-${type}`;
9496
this.executable = path.resolve(__dirname, '_test-double-benchmarker.js');
9597
this.present = fs.existsSync(this.executable);
9698
this.type = type;
9799
}
98100

99101
create(options) {
100-
const env = Object.assign({
101-
duration: options.duration,
102+
process.env.duration = process.env.duration || options.duration || 5;
103+
104+
const env = {
102105
test_url: `http://127.0.0.1:${options.port}${options.path}`,
103-
}, process.env);
106+
...process.env
107+
};
104108

105109
const child = child_process.fork(this.executable,
106110
[this.type],
@@ -189,13 +193,14 @@ http_benchmarkers.forEach((benchmarker) => {
189193
});
190194

191195
exports.run = function(options, callback) {
192-
options = Object.assign({
196+
options = {
193197
port: exports.PORT,
194198
path: '/',
195199
connections: 100,
196200
duration: 5,
197201
benchmarker: exports.default_http_benchmarker,
198-
}, options);
202+
...options
203+
};
199204
if (!options.benchmarker) {
200205
callback(new Error('Could not locate required http benchmarker. See ' +
201206
`${requirementsURL} for further instructions.`));
@@ -220,7 +225,8 @@ exports.run = function(options, callback) {
220225
child.stderr.pipe(process.stderr);
221226

222227
let stdout = '';
223-
child.stdout.on('data', (chunk) => stdout += chunk.toString());
228+
child.stdout.setEncoding('utf8');
229+
child.stdout.on('data', (chunk) => stdout += chunk);
224230

225231
child.once('close', (code) => {
226232
const elapsed = process.hrtime(benchmarker_start);

‎benchmark/_test-double-benchmarker.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if (!['http', 'http2'].includes(myModule)) {
77

88
const http = require(myModule);
99

10-
const duration = process.env.duration || 0;
10+
const duration = +process.env.duration;
1111
const url = process.env.test_url;
1212

1313
const start = process.hrtime();
@@ -18,13 +18,15 @@ function request(res, client) {
1818
res.on('error', () => {});
1919
res.on('end', () => {
2020
throughput++;
21-
const diff = process.hrtime(start);
22-
if (duration > 0 && diff[0] < duration) {
21+
const [sec, nanosec] = process.hrtime(start);
22+
const ms = sec * 1000 + nanosec / 1e6;
23+
if (ms < duration * 1000) {
2324
run();
2425
} else {
2526
console.log(JSON.stringify({ throughput }));
2627
if (client) {
2728
client.destroy();
29+
process.exit(0);
2830
}
2931
}
3032
});
@@ -33,7 +35,7 @@ function request(res, client) {
3335
function run() {
3436
if (http.get) { // HTTP
3537
http.get(url, request);
36-
} else { // HTTP/2
38+
} else { // HTTP/2
3739
const client = http.connect(url);
3840
client.on('error', (e) => { throw e; });
3941
request(client.request(), client);

‎benchmark/async_hooks/async-resource-vs-destroy.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ const {
1313
} = require('async_hooks');
1414
const { createServer } = require('http');
1515

16-
// Configuration for the http server
17-
// there is no need for parameters in this test
18-
const connections = 500;
19-
const path = '/';
20-
2116
const bench = common.createBenchmark(main, {
2217
type: ['async-resource', 'destroy', 'async-local-storage'],
2318
asyncMethod: ['callbacks', 'async'],
19+
path: '/',
20+
connections: 500,
21+
duration: 5,
2422
n: [1e6]
2523
});
2624

@@ -165,7 +163,7 @@ const asyncMethods = {
165163
'async': getServeAwait
166164
};
167165

168-
function main({ type, asyncMethod }) {
166+
function main({ type, asyncMethod, connections, duration, path }) {
169167
const { server, close } = types[type](asyncMethods[asyncMethod]);
170168

171169
server
@@ -174,7 +172,8 @@ function main({ type, asyncMethod }) {
174172

175173
bench.http({
176174
path,
177-
connections
175+
connections,
176+
duration
178177
}, () => {
179178
close();
180179
});

‎benchmark/async_hooks/http-server.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ const common = require('../common.js');
33

44
const bench = common.createBenchmark(main, {
55
asyncHooks: ['init', 'before', 'after', 'all', 'disabled', 'none'],
6-
connections: [50, 500]
6+
connections: [50, 500],
7+
duration: 5
78
});
89

9-
function main({ asyncHooks, connections }) {
10+
function main({ asyncHooks, connections, duration }) {
1011
if (asyncHooks !== 'none') {
1112
let hooks = {
1213
init() {},
@@ -33,6 +34,7 @@ function main({ asyncHooks, connections }) {
3334
bench.http({
3435
connections,
3536
path,
37+
duration
3638
}, () => {
3739
server.close();
3840
});

‎benchmark/buffers/buffer-base64-encode.js

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const common = require('../common.js');
2525
const bench = common.createBenchmark(main, {
2626
len: [64 * 1024 * 1024],
2727
n: [32]
28+
}, {
29+
test: { len: 256 }
2830
});
2931

3032
function main({ n, len }) {

‎benchmark/buffers/buffer-swap.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const bench = common.createBenchmark(main, {
77
method: ['swap16', 'swap32', 'swap64'/* , 'htons', 'htonl', 'htonll' */],
88
len: [64, 256, 768, 1024, 2056, 8192],
99
n: [1e6]
10+
}, {
11+
test: { len: 16 }
1012
});
1113

1214
// The htons and htonl methods below are used to benchmark the

‎benchmark/common.js

+107-70
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,83 @@ const child_process = require('child_process');
44
const http_benchmarkers = require('./_http-benchmarkers.js');
55

66
class Benchmark {
7-
constructor(fn, configs, options) {
8-
// Use the file name as the name of the benchmark
9-
this.name = require.main.filename.slice(__dirname.length + 1);
7+
// Used to make sure a benchmark only start a timer once
8+
#started = false;
9+
10+
// Indicate that the benchmark ended
11+
#ended = false;
12+
13+
// Holds process.hrtime value
14+
#time = [0, 0];
15+
16+
// Use the file name as the name of the benchmark
17+
name = require.main.filename.slice(__dirname.length + 1);
18+
19+
// Execution arguments i.e. flags used to run the jobs
20+
flags = process.env.NODE_BENCHMARK_FLAGS ?
21+
process.env.NODE_BENCHMARK_FLAGS.split(/\s+/) :
22+
[];
23+
24+
constructor(fn, configs, options = {}) {
1025
// Parse job-specific configuration from the command line arguments
11-
const parsed_args = this._parseArgs(process.argv.slice(2), configs);
26+
const argv = process.argv.slice(2);
27+
const parsed_args = this._parseArgs(argv, configs, options);
1228
this.options = parsed_args.cli;
1329
this.extra_options = parsed_args.extra;
30+
if (options.flags) {
31+
this.flags = this.flags.concat(options.flags);
32+
}
33+
1434
// The configuration list as a queue of jobs
1535
this.queue = this._queue(this.options);
36+
1637
// The configuration of the current job, head of the queue
1738
this.config = this.queue[0];
18-
// Execution arguments i.e. flags used to run the jobs
19-
this.flags = [];
20-
if (options && options.flags) {
21-
this.flags = this.flags.concat(options.flags);
22-
}
23-
if (process.env.NODE_BENCHMARK_FLAGS) {
24-
const flags = process.env.NODE_BENCHMARK_FLAGS.split(/\s+/);
25-
this.flags = this.flags.concat(flags);
26-
}
27-
// Holds process.hrtime value
28-
this._time = [0, 0];
29-
// Used to make sure a benchmark only start a timer once
30-
this._started = false;
31-
this._ended = false;
32-
33-
// this._run will use fork() to create a new process for each configuration
34-
// combination.
35-
if (process.env.hasOwnProperty('NODE_RUN_BENCHMARK_FN')) {
36-
process.nextTick(() => fn(this.config));
37-
} else {
38-
process.nextTick(() => this._run());
39-
}
39+
40+
process.nextTick(() => {
41+
if (process.env.hasOwnProperty('NODE_RUN_BENCHMARK_FN')) {
42+
fn(this.config);
43+
} else {
44+
// _run will use fork() to create a new process for each configuration
45+
// combination.
46+
this._run();
47+
}
48+
});
4049
}
4150

42-
_parseArgs(argv, configs) {
51+
_parseArgs(argv, configs, options) {
4352
const cliOptions = {};
53+
54+
// Check for the test mode first.
55+
const testIndex = argv.indexOf('--test');
56+
if (testIndex !== -1) {
57+
for (const [key, rawValue] of Object.entries(configs)) {
58+
let value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
59+
// Set numbers to one by default to reduce the runtime.
60+
if (typeof value === 'number') {
61+
if (key === 'dur' || key === 'duration') {
62+
value = 0.05;
63+
} else if (value > 1) {
64+
value = 1;
65+
}
66+
}
67+
cliOptions[key] = [value];
68+
}
69+
// Override specific test options.
70+
if (options.test) {
71+
for (const [key, value] of Object.entries(options.test)) {
72+
cliOptions[key] = Array.isArray(value) ? value : [value];
73+
}
74+
}
75+
argv.splice(testIndex, 1);
76+
} else {
77+
// Accept single values instead of arrays.
78+
for (const [key, value] of Object.entries(configs)) {
79+
if (!Array.isArray(value))
80+
configs[key] = [value];
81+
}
82+
}
83+
4484
const extraOptions = {};
4585
const validArgRE = /^(.+?)=([\s\S]*)$/;
4686
// Parse configuration arguments
@@ -50,45 +90,43 @@ class Benchmark {
5090
console.error(`bad argument: ${arg}`);
5191
process.exit(1);
5292
}
53-
const config = match[1];
54-
55-
if (configs[config]) {
56-
// Infer the type from the config object and parse accordingly
57-
const isNumber = typeof configs[config][0] === 'number';
58-
const value = isNumber ? +match[2] : match[2];
59-
if (!cliOptions[config])
60-
cliOptions[config] = [];
61-
cliOptions[config].push(value);
93+
const [, key, value] = match;
94+
if (Object.prototype.hasOwnProperty.call(configs, key)) {
95+
if (!cliOptions[key])
96+
cliOptions[key] = [];
97+
cliOptions[key].push(
98+
// Infer the type from the config object and parse accordingly
99+
typeof configs[key][0] === 'number' ? +value : value
100+
);
62101
} else {
63-
extraOptions[config] = match[2];
102+
extraOptions[key] = value;
64103
}
65104
}
66-
return { cli: Object.assign({}, configs, cliOptions), extra: extraOptions };
105+
return { cli: { ...configs, ...cliOptions }, extra: extraOptions };
67106
}
68107

69108
_queue(options) {
70109
const queue = [];
71110
const keys = Object.keys(options);
72111

73-
// Perform a depth-first walk though all options to generate a
112+
// Perform a depth-first walk through all options to generate a
74113
// configuration list that contains all combinations.
75114
function recursive(keyIndex, prevConfig) {
76115
const key = keys[keyIndex];
77116
const values = options[key];
78-
const type = typeof values[0];
79117

80118
for (const value of values) {
81119
if (typeof value !== 'number' && typeof value !== 'string') {
82120
throw new TypeError(
83121
`configuration "${key}" had type ${typeof value}`);
84122
}
85-
if (typeof value !== type) {
123+
if (typeof value !== typeof values[0]) {
86124
// This is a requirement for being able to consistently and
87125
// predictably parse CLI provided configuration values.
88126
throw new TypeError(`configuration "${key}" has mixed types`);
89127
}
90128

91-
const currConfig = Object.assign({ [key]: value }, prevConfig);
129+
const currConfig = { [key]: value, ...prevConfig };
92130

93131
if (keyIndex + 1 < keys.length) {
94132
recursive(keyIndex + 1, currConfig);
@@ -108,12 +146,11 @@ class Benchmark {
108146
}
109147

110148
http(options, cb) {
111-
const self = this;
112-
const http_options = Object.assign({ }, options);
149+
const http_options = { ...options };
113150
http_options.benchmarker = http_options.benchmarker ||
114-
self.config.benchmarker ||
115-
self.extra_options.benchmarker ||
116-
exports.default_http_benchmarker;
151+
this.config.benchmarker ||
152+
this.extra_options.benchmarker ||
153+
http_benchmarkers.default_http_benchmarker;
117154
http_benchmarkers.run(
118155
http_options, (error, code, used_benchmarker, result, elapsed) => {
119156
if (cb) {
@@ -123,14 +160,13 @@ class Benchmark {
123160
console.error(error);
124161
process.exit(code || 1);
125162
}
126-
self.config.benchmarker = used_benchmarker;
127-
self.report(result, elapsed);
163+
this.config.benchmarker = used_benchmarker;
164+
this.report(result, elapsed);
128165
}
129166
);
130167
}
131168

132169
_run() {
133-
const self = this;
134170
// If forked, report to the parent.
135171
if (process.send) {
136172
process.send({
@@ -140,57 +176,59 @@ class Benchmark {
140176
});
141177
}
142178

143-
(function recursive(queueIndex) {
144-
const config = self.queue[queueIndex];
179+
const recursive = (queueIndex) => {
180+
const config = this.queue[queueIndex];
145181

146182
// Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't
147183
// construct a configuration queue, but just execute the benchmark
148184
// function.
149-
const childEnv = Object.assign({}, process.env);
185+
const childEnv = { ...process.env };
150186
childEnv.NODE_RUN_BENCHMARK_FN = '';
151187

152188
// Create configuration arguments
153189
const childArgs = [];
154-
for (const key of Object.keys(config)) {
155-
childArgs.push(`${key}=${config[key]}`);
190+
for (const [key, value] of Object.entries(config)) {
191+
childArgs.push(`${key}=${value}`);
156192
}
157-
for (const key of Object.keys(self.extra_options)) {
158-
childArgs.push(`${key}=${self.extra_options[key]}`);
193+
for (const [key, value] of Object.entries(this.extra_options)) {
194+
childArgs.push(`${key}=${value}`);
159195
}
160196

161197
const child = child_process.fork(require.main.filename, childArgs, {
162198
env: childEnv,
163-
execArgv: self.flags.concat(process.execArgv),
199+
execArgv: this.flags.concat(process.execArgv),
164200
});
165201
child.on('message', sendResult);
166202
child.on('close', (code) => {
167203
if (code) {
168204
process.exit(code);
169205
}
170206

171-
if (queueIndex + 1 < self.queue.length) {
207+
if (queueIndex + 1 < this.queue.length) {
172208
recursive(queueIndex + 1);
173209
}
174210
});
175-
})(0);
211+
};
212+
213+
recursive(0);
176214
}
177215

178216
start() {
179-
if (this._started) {
217+
if (this.#started) {
180218
throw new Error('Called start more than once in a single benchmark');
181219
}
182-
this._started = true;
183-
this._time = process.hrtime();
220+
this.#started = true;
221+
this.#time = process.hrtime();
184222
}
185223

186224
end(operations) {
187225
// Get elapsed time now and do error checking later for accuracy.
188-
const elapsed = process.hrtime(this._time);
226+
const elapsed = process.hrtime(this.#time);
189227

190-
if (!this._started) {
228+
if (!this.#started) {
191229
throw new Error('called end without start');
192230
}
193-
if (this._ended) {
231+
if (this.#ended) {
194232
throw new Error('called end multiple times');
195233
}
196234
if (typeof operations !== 'number') {
@@ -206,7 +244,7 @@ class Benchmark {
206244
elapsed[1] = 1;
207245
}
208246

209-
this._ended = true;
247+
this.#ended = true;
210248
const time = elapsed[0] + elapsed[1] / 1e9;
211249
const rate = operations / time;
212250
this.report(rate, elapsed);
@@ -216,7 +254,7 @@ class Benchmark {
216254
sendResult({
217255
name: this.name,
218256
conf: this.config,
219-
rate: rate,
257+
rate,
220258
time: elapsed[0] + elapsed[1] / 1e9,
221259
type: 'report',
222260
});
@@ -334,6 +372,7 @@ function bakeUrlData(type, e = 0, withBase = false, asUrl = false) {
334372
}
335373

336374
module.exports = {
375+
Benchmark,
337376
PORT: http_benchmarkers.PORT,
338377
bakeUrlData,
339378
binding(bindingName) {
@@ -349,8 +388,6 @@ module.exports = {
349388
createBenchmark(fn, configs, options) {
350389
return new Benchmark(fn, configs, options);
351390
},
352-
// Benchmark an http server.
353-
default_http_benchmarker: http_benchmarkers.default_http_benchmarker,
354391
sendResult,
355392
searchParams,
356393
urlDataTypes: Object.keys(urls).concat(['wpt']),

‎benchmark/compare.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const BenchmarkProgress = require('./_benchmark_progress.js');
99
//
1010
// Parse arguments
1111
//
12-
const cli = CLI(`usage: ./node compare.js [options] [--] <category> ...
12+
const cli = new CLI(`usage: ./node compare.js [options] [--] <category> ...
1313
Run each benchmark in the <category> directory many times using two different
1414
node versions. More than one <category> directory can be specified.
1515
The output is formatted as csv, which can be processed using for

‎benchmark/crypto/cipher-stream.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const bench = common.createBenchmark(main, {
77
type: ['asc', 'utf', 'buf'],
88
len: [2, 1024, 102400, 1024 * 1024],
99
api: ['legacy', 'stream']
10+
}, {
11+
flags: ['--no-warnings']
1012
});
1113

1214
function main({ api, cipher, type, len, writes }) {

‎benchmark/fs/read-stream-throughput.js

+35-40
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,18 @@ tmpdir.refresh();
1111
const filename = path.resolve(tmpdir.path,
1212
`.removeme-benchmark-garbage-${process.pid}`);
1313

14-
let encodingType, encoding, size, filesize;
15-
1614
const bench = common.createBenchmark(main, {
1715
encodingType: ['buf', 'asc', 'utf'],
18-
filesize: [1000 * 1024 * 1024],
19-
size: [1024, 4096, 65535, 1024 * 1024]
16+
filesize: [1000 * 1024],
17+
highWaterMark: [1024, 4096, 65535, 1024 * 1024],
18+
n: 1024
2019
});
2120

2221
function main(conf) {
23-
encodingType = conf.encodingType;
24-
size = conf.size;
25-
filesize = conf.filesize;
22+
const { encodingType, highWaterMark, filesize } = conf;
23+
let { n } = conf;
2624

25+
let encoding = '';
2726
switch (encodingType) {
2827
case 'buf':
2928
encoding = null;
@@ -38,34 +37,8 @@ function main(conf) {
3837
throw new Error(`invalid encodingType: ${encodingType}`);
3938
}
4039

41-
makeFile();
42-
}
43-
44-
function runTest() {
45-
assert(fs.statSync(filename).size === filesize);
46-
const rs = fs.createReadStream(filename, {
47-
highWaterMark: size,
48-
encoding: encoding
49-
});
50-
51-
rs.on('open', () => {
52-
bench.start();
53-
});
54-
55-
let bytes = 0;
56-
rs.on('data', (chunk) => {
57-
bytes += chunk.length;
58-
});
59-
60-
rs.on('end', () => {
61-
try { fs.unlinkSync(filename); } catch {}
62-
// MB/sec
63-
bench.end(bytes / (1024 * 1024));
64-
});
65-
}
66-
67-
function makeFile() {
68-
const buf = Buffer.allocUnsafe(filesize / 1024);
40+
// Make file
41+
const buf = Buffer.allocUnsafe(filesize);
6942
if (encoding === 'utf8') {
7043
// ü
7144
for (let i = 0; i < buf.length; i++) {
@@ -78,16 +51,38 @@ function makeFile() {
7851
}
7952

8053
try { fs.unlinkSync(filename); } catch {}
81-
let w = 1024;
8254
const ws = fs.createWriteStream(filename);
83-
ws.on('close', runTest);
55+
ws.on('close', runTest.bind(null, filesize, highWaterMark, encoding, n));
8456
ws.on('drain', write);
8557
write();
8658
function write() {
8759
do {
88-
w--;
89-
} while (false !== ws.write(buf) && w > 0);
90-
if (w === 0)
60+
n--;
61+
} while (false !== ws.write(buf) && n > 0);
62+
if (n === 0)
9163
ws.end();
9264
}
9365
}
66+
67+
function runTest(filesize, highWaterMark, encoding, n) {
68+
assert(fs.statSync(filename).size === filesize * n);
69+
const rs = fs.createReadStream(filename, {
70+
highWaterMark,
71+
encoding
72+
});
73+
74+
rs.on('open', () => {
75+
bench.start();
76+
});
77+
78+
let bytes = 0;
79+
rs.on('data', (chunk) => {
80+
bytes += chunk.length;
81+
});
82+
83+
rs.on('end', () => {
84+
try { fs.unlinkSync(filename); } catch {}
85+
// MB/sec
86+
bench.end(bytes / (1024 * 1024));
87+
});
88+
}

‎benchmark/fs/readfile.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ const filename = path.resolve(tmpdir.path,
1414
`.removeme-benchmark-garbage-${process.pid}`);
1515

1616
const bench = common.createBenchmark(main, {
17-
dur: [5],
17+
duration: [5],
1818
len: [1024, 16 * 1024 * 1024],
1919
concurrent: [1, 10]
2020
});
2121

22-
function main({ len, dur, concurrent }) {
22+
function main({ len, duration, concurrent }) {
2323
try { fs.unlinkSync(filename); } catch {}
2424
let data = Buffer.alloc(len, 'x');
2525
fs.writeFileSync(filename, data);
@@ -33,7 +33,7 @@ function main({ len, dur, concurrent }) {
3333
bench.end(reads);
3434
try { fs.unlinkSync(filename); } catch {}
3535
process.exit(0);
36-
}, dur * 1000);
36+
}, duration * 1000);
3737

3838
function read() {
3939
fs.readFile(filename, afterRead);

‎benchmark/http/chunked.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ const common = require('../common.js');
1313
const bench = common.createBenchmark(main, {
1414
n: [1, 4, 8, 16],
1515
len: [1, 64, 256],
16-
c: [100]
16+
c: [100],
17+
duration: 5
1718
});
1819

19-
function main({ len, n, c }) {
20+
function main({ len, n, c, duration }) {
2021
const http = require('http');
2122
const chunk = Buffer.alloc(len, '8');
2223

@@ -33,7 +34,8 @@ function main({ len, n, c }) {
3334

3435
server.listen(common.PORT, () => {
3536
bench.http({
36-
connections: c
37+
connections: c,
38+
duration
3739
}, () => {
3840
server.close();
3941
});

‎benchmark/http/cluster.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ if (cluster.isMaster) {
99
// Unicode confuses ab on os x.
1010
type: ['bytes', 'buffer'],
1111
len: [4, 1024, 102400],
12-
c: [50, 500]
12+
c: [50, 500],
13+
duration: 5,
1314
});
1415
} else {
1516
const port = parseInt(process.env.PORT || PORT);
1617
require('../fixtures/simple-http-server.js').listen(port);
1718
}
1819

19-
function main({ type, len, c }) {
20+
function main({ type, len, c, duration }) {
2021
process.env.PORT = PORT;
2122
let workers = 0;
2223
const w1 = cluster.fork();
@@ -32,7 +33,8 @@ function main({ type, len, c }) {
3233

3334
bench.http({
3435
path: path,
35-
connections: c
36+
connections: c,
37+
duration
3638
}, () => {
3739
w1.destroy();
3840
w2.destroy();

‎benchmark/http/end-vs-write-end.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ const bench = common.createBenchmark(main, {
1414
type: ['asc', 'utf', 'buf'],
1515
len: [64 * 1024, 128 * 1024, 256 * 1024, 1024 * 1024],
1616
c: [100],
17-
method: ['write', 'end']
17+
method: ['write', 'end'],
18+
duration: 5
1819
});
1920

20-
function main({ len, type, method, c }) {
21+
function main({ len, type, method, c, duration }) {
2122
const http = require('http');
2223
let chunk;
2324
switch (type) {
@@ -49,7 +50,8 @@ function main({ len, type, method, c }) {
4950

5051
server.listen(common.PORT, () => {
5152
bench.http({
52-
connections: c
53+
connections: c,
54+
duration
5355
}, () => {
5456
server.close();
5557
});

‎benchmark/http/headers.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ const http = require('http');
66
const bench = common.createBenchmark(main, {
77
n: [10, 1000],
88
len: [1, 100],
9+
duration: 5
910
});
1011

11-
function main({ len, n }) {
12+
function main({ len, n, duration }) {
1213
const headers = {
1314
'Connection': 'keep-alive',
1415
'Transfer-Encoding': 'chunked',
@@ -29,7 +30,8 @@ function main({ len, n }) {
2930
server.listen(common.PORT, () => {
3031
bench.http({
3132
path: '/',
32-
connections: 10
33+
connections: 10,
34+
duration
3335
}, () => {
3436
server.close();
3537
});

‎benchmark/http/incoming_headers.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ const common = require('../common.js');
33
const http = require('http');
44

55
const bench = common.createBenchmark(main, {
6-
c: [50], // Concurrent connections
7-
n: [20], // Number of header lines to append after the common headers
8-
w: [0, 6], // Amount of trailing whitespace
6+
connections: [50], // Concurrent connections
7+
headers: [20], // Number of header lines to append after the common headers
8+
w: [0, 6], // Amount of trailing whitespace
9+
duration: 5
910
});
1011

11-
function main({ c, n, w }) {
12+
function main({ connections, headers, w, duration }) {
1213
const server = http.createServer((req, res) => {
1314
res.end();
1415
});
@@ -21,7 +22,7 @@ function main({ c, n, w }) {
2122
'Date': new Date().toString(),
2223
'Cache-Control': 'no-cache'
2324
};
24-
for (let i = 0; i < n; i++) {
25+
for (let i = 0; i < headers; i++) {
2526
// Note:
2627
// - autocannon does not send header values with OWS
2728
// - wrk can only send trailing OWS. This is a side-effect of wrk
@@ -31,8 +32,9 @@ function main({ c, n, w }) {
3132
}
3233
bench.http({
3334
path: '/',
34-
connections: c,
35-
headers
35+
connections,
36+
headers,
37+
duration
3638
}, () => {
3739
server.close();
3840
});

‎benchmark/http/set-header.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const common = require('../common.js');
33
const PORT = common.PORT;
44

55
const bench = common.createBenchmark(main, {
6-
res: ['normal', 'setHeader', 'setHeaderWH']
6+
res: ['normal', 'setHeader', 'setHeaderWH'],
7+
duration: 5
78
});
89

910
const type = 'bytes';
@@ -15,16 +16,17 @@ const c = 50;
1516
// normal: writeHead(status, {...})
1617
// setHeader: statusCode = status, setHeader(...) x2
1718
// setHeaderWH: setHeader(...), writeHead(status, ...)
18-
function main({ res }) {
19+
function main({ res, duration }) {
1920
process.env.PORT = PORT;
2021
const server = require('../fixtures/simple-http-server.js')
2122
.listen(PORT)
2223
.on('listening', () => {
23-
const path = `/${type}/${len}/${chunks}/normal/${chunkedEnc}`;
24+
const path = `/${type}/${len}/${chunks}/${res}/${chunkedEnc}`;
2425

2526
bench.http({
2627
path: path,
27-
connections: c
28+
connections: c,
29+
duration
2830
}, () => {
2931
server.close();
3032
});

‎benchmark/http/simple.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ const bench = common.createBenchmark(main, {
77
len: [4, 1024, 102400],
88
chunks: [1, 4],
99
c: [50, 500],
10-
chunkedEnc: [1, 0]
10+
chunkedEnc: [1, 0],
11+
duration: 5
1112
});
1213

13-
function main({ type, len, chunks, c, chunkedEnc, res }) {
14+
function main({ type, len, chunks, c, chunkedEnc, duration }) {
1415
const server = require('../fixtures/simple-http-server.js')
1516
.listen(common.PORT)
1617
.on('listening', () => {
1718
const path = `/${type}/${len}/${chunks}/normal/${chunkedEnc}`;
1819

1920
bench.http({
20-
path: path,
21-
connections: c
21+
path,
22+
connections: c,
23+
duration
2224
}, () => {
2325
server.close();
2426
});

‎benchmark/http2/compat.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const bench = common.createBenchmark(main, {
99
requests: [100, 1000, 5000],
1010
streams: [1, 10, 20, 40, 100, 200],
1111
clients: [2],
12-
benchmarker: ['h2load']
12+
benchmarker: ['test-double-http2'],
13+
duration: 5
1314
}, { flags: ['--no-warnings'] });
1415

15-
function main({ requests, streams, clients }) {
16+
function main({ requests, streams, clients, duration }) {
1617
const http2 = require('http2');
1718
const server = http2.createServer();
1819
server.on('request', (req, res) => {
@@ -29,7 +30,8 @@ function main({ requests, streams, clients }) {
2930
requests,
3031
maxConcurrentStreams: streams,
3132
clients,
32-
threads: clients
33+
threads: clients,
34+
duration
3335
}, () => { server.close(); });
3436
});
3537
}

‎benchmark/http2/respond-with-fd.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ const bench = common.createBenchmark(main, {
1010
requests: [100, 1000, 5000],
1111
streams: [1, 10, 20, 40, 100, 200],
1212
clients: [2],
13-
benchmarker: ['h2load']
13+
benchmarker: ['test-double-http2'],
14+
duration: 5
1415
}, { flags: ['--no-warnings'] });
1516

16-
function main({ requests, streams, clients }) {
17+
function main({ requests, streams, clients, duration }) {
1718
fs.open(file, 'r', (err, fd) => {
1819
if (err)
1920
throw err;
@@ -30,6 +31,7 @@ function main({ requests, streams, clients }) {
3031
requests,
3132
maxConcurrentStreams: streams,
3233
clients,
34+
duration,
3335
threads: clients
3436
}, () => server.close());
3537
});

‎benchmark/http2/simple.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const bench = common.createBenchmark(main, {
99
requests: [100, 1000, 5000],
1010
streams: [1, 10, 20, 40, 100, 200],
1111
clients: [2],
12-
benchmarker: ['h2load']
12+
benchmarker: ['test-double-http2'],
13+
duration: 5
1314
}, { flags: ['--no-warnings'] });
1415

15-
function main({ requests, streams, clients }) {
16+
function main({ requests, streams, clients, duration }) {
1617
const http2 = require('http2');
1718
const server = http2.createServer();
1819
server.on('stream', (stream) => {
@@ -27,6 +28,7 @@ function main({ requests, streams, clients }) {
2728
requests,
2829
maxConcurrentStreams: streams,
2930
clients,
31+
duration,
3032
threads: clients
3133
}, () => { server.close(); });
3234
});

‎benchmark/http2/write.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ const bench = common.createBenchmark(main, {
66
streams: [100, 200, 1000],
77
length: [64 * 1024, 128 * 1024, 256 * 1024, 1024 * 1024],
88
size: [100000],
9-
benchmarker: ['h2load']
9+
benchmarker: ['test-double-http2'],
10+
duration: 5
1011
}, { flags: ['--no-warnings'] });
1112

12-
function main({ streams, length, size }) {
13+
function main({ streams, length, size, duration }) {
1314
const http2 = require('http2');
1415
const server = http2.createServer();
1516
server.on('stream', (stream) => {
@@ -29,6 +30,7 @@ function main({ streams, length, size }) {
2930
bench.http({
3031
path: '/',
3132
requests: 10000,
33+
duration,
3234
maxConcurrentStreams: streams,
3335
}, () => { server.close(); });
3436
});

‎benchmark/misc/trace.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ const bench = common.createBenchmark(main, {
66
n: [100000],
77
method: ['trace', 'isTraceCategoryEnabled']
88
}, {
9-
flags: ['--expose-internals', '--trace-event-categories', 'foo']
9+
flags: [
10+
'--expose-internals',
11+
'--no-warnings',
12+
'--trace-event-categories', 'foo',
13+
]
1014
});
1115

1216
const {

‎benchmark/net/net-c2s.js

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const bench = common.createBenchmark(main, {
99
len: [64, 102400, 1024 * 1024 * 16],
1010
type: ['utf', 'asc', 'buf'],
1111
dur: [5],
12+
}, {
13+
test: { len: 1024 }
1214
});
1315

1416
let chunk;

‎benchmark/net/net-pipe.js

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const bench = common.createBenchmark(main, {
99
len: [2, 64, 102400, 1024 * 1024 * 16],
1010
type: ['utf', 'asc', 'buf'],
1111
dur: [5],
12+
}, {
13+
test: { len: 1024 }
1214
});
1315

1416
let chunk;

‎benchmark/net/net-s2c.js

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const bench = common.createBenchmark(main, {
1010
recvbuflen: [0, 64 * 1024, 1024 * 1024],
1111
recvbufgenfn: ['true', 'false'],
1212
dur: [5]
13+
}, {
14+
test: { sendchunklen: 256 }
1315
});
1416

1517
let chunk;

‎benchmark/net/net-wrap-js-stream-passthrough.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const bench = common.createBenchmark(main, {
99
type: ['utf', 'asc', 'buf'],
1010
dur: [5],
1111
}, {
12+
test: { len: 64 },
1213
flags: ['--expose-internals']
1314
});
1415

‎benchmark/net/tcp-raw-c2s.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ const bench = common.createBenchmark(main, {
1212
len: [102400, 1024 * 1024 * 16],
1313
type: ['utf', 'asc', 'buf'],
1414
dur: [5]
15-
}, { flags: [ '--expose-internals', '--no-warnings' ] });
15+
}, {
16+
test: { len: 1024 },
17+
flags: [ '--expose-internals', '--no-warnings' ]
18+
});
1619

1720
function main({ dur, len, type }) {
1821
const {

‎benchmark/net/tcp-raw-pipe.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const bench = common.createBenchmark(main, {
1313
type: ['utf', 'asc', 'buf'],
1414
dur: [5]
1515
}, {
16+
test: { len: 1024 },
1617
flags: [ '--expose-internals', '--no-warnings' ]
1718
});
1819

‎benchmark/net/tcp-raw-s2c.js

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const bench = common.createBenchmark(main, {
1313
type: ['utf', 'asc', 'buf'],
1414
dur: [5]
1515
}, {
16+
test: { len: 1024 },
1617
flags: [ '--expose-internals', '--no-warnings' ]
1718
});
1819

‎benchmark/run.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const path = require('path');
44
const fork = require('child_process').fork;
55
const CLI = require('./_cli.js');
66

7-
const cli = CLI(`usage: ./node run.js [options] [--] <category> ...
7+
const cli = new CLI(`usage: ./node run.js [options] [--] <category> ...
88
Run each benchmark in the <category> directory a single time, more than one
99
<category> directory can be specified.
1010
@@ -14,6 +14,9 @@ const cli = CLI(`usage: ./node run.js [options] [--] <category> ...
1414
repeated)
1515
--set variable=value set benchmark variable (can be repeated)
1616
--format [simple|csv] optional value that specifies the output format
17+
test only run a single configuration from the options
18+
matrix
19+
all each benchmark category is run one after the other
1720
`, { arrayArgs: ['set', 'filter', 'exclude'] });
1821
const benchmarks = cli.benchmarks();
1922

@@ -37,7 +40,11 @@ if (format === 'csv') {
3740

3841
(function recursive(i) {
3942
const filename = benchmarks[i];
40-
const child = fork(path.resolve(__dirname, filename), cli.optional.set);
43+
const child = fork(
44+
path.resolve(__dirname, filename),
45+
cli.test ? ['--test'] : [],
46+
cli.optional.set
47+
);
4148

4249
if (format !== 'csv') {
4350
console.log();
@@ -51,10 +58,10 @@ if (format === 'csv') {
5158
// Construct configuration string, " A=a, B=b, ..."
5259
let conf = '';
5360
for (const key of Object.keys(data.conf)) {
54-
conf += ` ${key}=${JSON.stringify(data.conf[key])}`;
61+
if (conf !== '')
62+
conf += ' ';
63+
conf += `${key}=${JSON.stringify(data.conf[key])}`;
5564
}
56-
// Delete first space of the configuration
57-
conf = conf.slice(1);
5865
if (format === 'csv') {
5966
// Escape quotes (") for correct csv formatting
6067
conf = conf.replace(/"/g, '""');

‎benchmark/scatter.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const CLI = require('./_cli.js');
77
//
88
// Parse arguments
99
//
10-
const cli = CLI(`usage: ./node scatter.js [options] [--] <filename>
10+
const cli = new CLI(`usage: ./node scatter.js [options] [--] <filename>
1111
Run the benchmark script <filename> many times and output the rate (ops/s)
1212
together with the benchmark variables as a csv.
1313

‎benchmark/tls/secure-pair.js

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const bench = common.createBenchmark(main, {
44
dur: [5],
55
securing: ['SecurePair', 'TLSSocket', 'clear'],
66
size: [100, 1024, 1024 * 1024]
7+
}, {
8+
flags: ['--no-warnings']
79
});
810

911
const fixtures = require('../../test/common/fixtures');

‎benchmark/util/type-check.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const bench = common.createBenchmark(main, {
3131
argument: ['true', 'false-primitive', 'false-object'],
3232
n: [1e5]
3333
}, {
34-
flags: ['--expose-internals']
34+
flags: ['--expose-internals', '--no-warnings']
3535
});
3636

3737
function main({ type, argument, version, n }) {

‎benchmark/zlib/pipe.js

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ const bench = common.createBenchmark(main, {
88
duration: [5],
99
type: ['string', 'buffer'],
1010
algorithm: ['gzip', 'brotli']
11+
}, {
12+
test: {
13+
inputLen: 1024,
14+
duration: 0.2
15+
}
1116
});
1217

1318
function main({ inputLen, duration, type, algorithm }) {

‎doc/guides/writing-and-running-benchmarks.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,8 @@ const common = require('../common.js');
519519

520520
const bench = common.createBenchmark(main, {
521521
kb: [64, 128, 256, 1024],
522-
connections: [100, 500]
522+
connections: [100, 500],
523+
duration: 5
523524
});
524525

525526
function main(conf) {
@@ -546,8 +547,8 @@ Supported options keys are:
546547
* `path` - defaults to `/`
547548
* `connections` - number of concurrent connections to use, defaults to 100
548549
* `duration` - duration of the benchmark in seconds, defaults to 10
549-
* `benchmarker` - benchmarker to use, defaults to
550-
`common.default_http_benchmarker`
550+
* `benchmarker` - benchmarker to use, defaults to the first available http
551+
benchmarker
551552

552553
[autocannon]: https://github.com/mcollina/autocannon
553554
[wrk]: https://github.com/wg/wrk

‎test/common/benchmark.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,8 @@ const path = require('path');
88

99
const runjs = path.join(__dirname, '..', '..', 'benchmark', 'run.js');
1010

11-
function runBenchmark(name, args, env) {
12-
const argv = [];
13-
14-
for (let i = 0; i < args.length; i++) {
15-
argv.push('--set');
16-
argv.push(args[i]);
17-
}
11+
function runBenchmark(name, env) {
12+
const argv = ['test'];
1813

1914
argv.push(name);
2015

0 commit comments

Comments
 (0)
Please sign in to comment.