Skip to content

Commit 19bf49e

Browse files
puskin94Mesteery
authored andcommitted
benchmark: adds groups to better separate benchmarks
Fixes: #26425 Co-Authored-By: Yaman Kassir <[email protected]> PR-URL: #54393 Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]>
1 parent fc7c170 commit 19bf49e

File tree

4 files changed

+89
-27
lines changed

4 files changed

+89
-27
lines changed

benchmark/common.js

+20-11
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,36 @@ class Benchmark {
2222
this.name = require.main.filename.slice(__dirname.length + 1);
2323

2424
// Execution arguments i.e. flags used to run the jobs
25-
this.flags = process.env.NODE_BENCHMARK_FLAGS ?
26-
process.env.NODE_BENCHMARK_FLAGS.split(/\s+/) :
27-
[];
25+
this.flags = process.env.NODE_BENCHMARK_FLAGS?.split(/\s+/) ?? [];
2826

2927
// Parse job-specific configuration from the command line arguments
3028
const argv = process.argv.slice(2);
3129
const parsed_args = this._parseArgs(argv, configs, options);
30+
3231
this.options = parsed_args.cli;
3332
this.extra_options = parsed_args.extra;
33+
this.combinationFilter = typeof options.combinationFilter === 'function' ? options.combinationFilter : allow;
34+
35+
if (options.byGroups) {
36+
this.queue = [];
37+
const groupNames = process.env.NODE_RUN_BENCHMARK_GROUPS?.split(',') ?? Object.keys(configs);
38+
39+
for (const groupName of groupNames) {
40+
const config = { ...configs[groupName][0], group: groupName };
41+
const parsed_args = this._parseArgs(argv, config, options);
42+
43+
this.options = parsed_args.cli;
44+
this.extra_options = parsed_args.extra;
45+
this.queue = this.queue.concat(this._queue(this.options));
46+
}
47+
} else {
48+
this.queue = this._queue(this.options);
49+
}
50+
3451
if (options.flags) {
3552
this.flags = this.flags.concat(options.flags);
3653
}
3754

38-
if (typeof options.combinationFilter === 'function')
39-
this.combinationFilter = options.combinationFilter;
40-
else
41-
this.combinationFilter = allow;
42-
43-
// The configuration list as a queue of jobs
44-
this.queue = this._queue(this.options);
45-
4655
if (this.queue.length === 0)
4756
return;
4857

benchmark/http/headers.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,32 @@ const common = require('../common.js');
44
const http = require('http');
55

66
const bench = common.createBenchmark(main, {
7-
n: [10, 600],
8-
len: [1, 100],
9-
duration: 5,
10-
});
7+
fewHeaders: {
8+
n: [10],
9+
len: [1, 5],
10+
duration: 5,
11+
},
12+
mediumHeaders: {
13+
n: [50],
14+
len: [1, 10],
15+
duration: 5,
16+
},
17+
manyHeaders: {
18+
n: [600],
19+
len: [1, 100],
20+
duration: 5,
21+
},
22+
}, { byGroups: true });
1123

1224
function main({ len, n, duration }) {
1325
const headers = {
1426
'Connection': 'keep-alive',
1527
'Transfer-Encoding': 'chunked',
1628
};
1729

18-
// TODO(BridgeAR): Change this benchmark to use grouped arguments when
19-
// implemented. https://github.com/nodejs/node/issues/26425
20-
const Is = [ ...Array(Math.max(n / len, 1)).keys() ];
21-
const Js = [ ...Array(len).keys() ];
30+
const Is = [...Array(n / len).keys()];
31+
const Js = [...Array(len).keys()];
32+
2233
for (const i of Is) {
2334
headers[`foo${i}`] = Js.map(() => `some header value ${i}`);
2435
}
@@ -27,6 +38,7 @@ function main({ len, n, duration }) {
2738
res.writeHead(200, headers);
2839
res.end();
2940
});
41+
3042
server.listen(0, () => {
3143
bench.http({
3244
path: '/',

doc/contributing/writing-and-running-benchmarks.md

+30
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ process/bench-env.js operation="query" n=1000000: 3,625,787.2150573144
272272
process/bench-env.js operation="delete" n=1000000: 1,521,131.5742806569
273273
```
274274

275+
#### Grouping benchmarks
276+
277+
Benchmarks can also have groups, giving the developer greater flexibility in differentiating between test cases
278+
and also helping reduce the time to run the combination of benchmark parameters.
279+
280+
By default, all groups are executed when running the benchmark.
281+
However, it is possible to specify individual groups by setting the
282+
`NODE_RUN_BENCHMARK_GROUPS` environment variable when running `compare.js`:
283+
284+
```bash
285+
NODE_RUN_BENCHMARK_GROUPS=fewHeaders,manyHeaders node http/headers.js
286+
```
287+
275288
### Comparing Node.js versions
276289

277290
To compare the effect of a new Node.js version use the `compare.js` tool. This
@@ -492,6 +505,23 @@ The arguments of `createBenchmark` are:
492505
* `options` {Object} The benchmark options. Supported options:
493506
* `flags` {Array} Contains node-specific command line flags to pass to
494507
the child process.
508+
509+
* `byGroups` {Boolean} option for processing `configs` by groups:
510+
```js
511+
const bench = common.createBenchmark(main, {
512+
groupA: {
513+
source: ['array'],
514+
len: [10, 2048],
515+
n: [50],
516+
},
517+
groupB: {
518+
source: ['buffer', 'string'],
519+
len: [2048],
520+
n: [50, 2048],
521+
},
522+
}, { byGroups: true });
523+
```
524+
495525
* `combinationFilter` {Function} Has a single parameter which is an object
496526
containing a combination of benchmark parameters. It should return `true`
497527
or `false` to indicate whether the combination should be included or not.

test/common/benchmark.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,27 @@ function runBenchmark(name, env) {
2727
child.on('exit', (code, signal) => {
2828
assert.strictEqual(code, 0);
2929
assert.strictEqual(signal, null);
30+
3031
// This bit makes sure that each benchmark file is being sent settings such
3132
// that the benchmark file runs just one set of options. This helps keep the
32-
// benchmark tests from taking a long time to run. Therefore, each benchmark
33-
// file should result in three lines of output: a blank line, a line with
34-
// the name of the benchmark file, and a line with the only results that we
35-
// get from testing the benchmark file.
36-
assert.ok(
37-
/^(?:\n.+?\n.+?\n)+$/.test(stdout),
38-
`benchmark file not running exactly one configuration in test: ${stdout}`,
39-
);
33+
// benchmark tests from taking a long time to run. Therefore, stdout should be composed as follows:
34+
// The first and last lines should be empty.
35+
// Each test should be separated by a blank line.
36+
// The first line of each test should contain the test's name.
37+
// The second line of each test should contain the configuration for the test.
38+
// If the test configuration is not a group, there should be exactly two lines.
39+
// Otherwise, it is possible to have more than two lines.
40+
41+
const splitTests = stdout.split(/\n\s*\n/);
42+
43+
for (let testIdx = 1; testIdx < splitTests.length - 1; testIdx++) {
44+
const lines = splitTests[testIdx].split('\n');
45+
assert.ok(/.+/.test(lines[0]));
46+
47+
if (!lines[1].includes('group="')) {
48+
assert.strictEqual(lines.length, 2, `benchmark file not running exactly one configuration in test: ${stdout}`);
49+
}
50+
}
4051
});
4152
}
4253

0 commit comments

Comments
 (0)