Skip to content

Commit 022c743

Browse files
joyeecheungevanlucas
authored andcommitted
benchmark: allow benchmarks to specify flags
* Give createBenchmark and the Benchmark constructor a third argument for specifying the command line flags that this benchmark should be run with. The benchmarks are no longer run with --expose-internals by default, they will need to explicitly pass the flags. * Rename options to configs in createBenchmark and the Benchmark constructor to match the documentation since they are not optional. * Comment the properties of a Benchmark object Also improve the documentation about creating benchmarks * Add detailed description of the arguments of `createBenchmark` * Describe the two passes of running the benchmarks * Suggest what kind of code should go where in the benchmark example PR-URL: #10448 Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net>
1 parent 66a9f01 commit 022c743

File tree

3 files changed

+81
-25
lines changed

3 files changed

+81
-25
lines changed

benchmark/README.md

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,37 +287,84 @@ chunk encoding mean confidence.interval
287287
## Creating a benchmark
288288

289289
All benchmarks use the `require('../common.js')` module. This contains the
290-
`createBenchmark(main, configs)` method which will setup your benchmark.
290+
`createBenchmark(main, configs[, options])` method which will setup your
291+
benchmark.
291292

292-
The first argument `main` is the benchmark function, the second argument
293-
specifies the benchmark parameters. `createBenchmark` will run all possible
294-
combinations of these parameters, unless specified otherwise. Note that the
295-
configuration values can only be strings or numbers.
293+
The arguments of `createBenchmark` are:
296294

297-
`createBenchmark` also creates a `bench` object, which is used for timing
295+
* `main` {Function} The benchmark function,
296+
where the code running operations and controlling timers should go
297+
* `configs` {Object} The benchmark parameters. `createBenchmark` will run all
298+
possible combinations of these parameters, unless specified otherwise.
299+
Each configuration is a property with an array of possible values.
300+
Note that the configuration values can only be strings or numbers.
301+
* `options` {Object} The benchmark options. At the moment only the `flags`
302+
option for specifying command line flags is supported.
303+
304+
`createBenchmark` returns a `bench` object, which is used for timing
298305
the runtime of the benchmark. Run `bench.start()` after the initialization
299306
and `bench.end(n)` when the benchmark is done. `n` is the number of operations
300307
you performed in the benchmark.
301308

309+
The benchmark script will be run twice:
310+
311+
The first pass will configure the benchmark with the combination of
312+
parameters specified in `configs`, and WILL NOT run the `main` function.
313+
In this pass, no flags except the ones directly passed via commands
314+
that you run the benchmarks with will be used.
315+
316+
In the second pass, the `main` function will be run, and the process
317+
will be launched with:
318+
319+
* The flags you've passed into `createBenchmark` (the third argument)
320+
* The flags in the command that you run this benchmark with
321+
322+
Beware that any code outside the `main` function will be run twice
323+
in different processes. This could be troublesome if the code
324+
outside the `main` function has side effects. In general, prefer putting
325+
the code inside the `main` function if it's more than just declaration.
326+
302327
```js
303328
'use strict';
304329
const common = require('../common.js');
305330
const SlowBuffer = require('buffer').SlowBuffer;
306331

307-
const bench = common.createBenchmark(main, {
332+
const configs = {
333+
// Number of operations, specified here so they show up in the report.
334+
// Most benchmarks just use one value for all runs.
308335
n: [1024],
309-
type: ['fast', 'slow'],
310-
size: [16, 128, 1024]
311-
});
336+
type: ['fast', 'slow'], // Custom configurations
337+
size: [16, 128, 1024] // Custom configurations
338+
};
339+
340+
const options = {
341+
// Add --expose-internals if you want to require internal modules in main
342+
flags: ['--zero-fill-buffers']
343+
};
344+
345+
// main and configs are required, options is optional.
346+
const bench = common.createBenchmark(main, configs, options);
347+
348+
// Note that any code outside main will be run twice,
349+
// in different processes, with different command line arguments.
312350

313351
function main(conf) {
352+
// You will only get the flags that you have passed to createBenchmark
353+
// earlier when main is run. If you want to benchmark the internal modules,
354+
// require them here. For example:
355+
// const URL = require('internal/url').URL
356+
357+
// Start the timer
314358
bench.start();
315359

360+
// Do operations here
316361
const BufferConstructor = conf.type === 'fast' ? Buffer : SlowBuffer;
317362

318363
for (let i = 0; i < conf.n; i++) {
319364
new BufferConstructor(conf.size);
320365
}
366+
367+
// End the timer, pass in the number of operations
321368
bench.end(conf.n);
322369
}
323370
```

benchmark/common.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,29 @@
33
const child_process = require('child_process');
44
const http_benchmarkers = require('./_http-benchmarkers.js');
55

6-
exports.createBenchmark = function(fn, options) {
7-
return new Benchmark(fn, options);
6+
exports.createBenchmark = function(fn, configs, options) {
7+
return new Benchmark(fn, configs, options);
88
};
99

10-
function Benchmark(fn, options) {
10+
function Benchmark(fn, configs, options) {
11+
// Use the file name as the name of the benchmark
1112
this.name = require.main.filename.slice(__dirname.length + 1);
12-
const parsed_args = this._parseArgs(process.argv.slice(2), options);
13+
// Parse job-specific configuration from the command line arguments
14+
const parsed_args = this._parseArgs(process.argv.slice(2), configs);
1315
this.options = parsed_args.cli;
1416
this.extra_options = parsed_args.extra;
17+
// The configuration list as a queue of jobs
1518
this.queue = this._queue(this.options);
19+
// The configuration of the current job, head of the queue
1620
this.config = this.queue[0];
17-
18-
this._time = [0, 0]; // holds process.hrtime value
21+
// Execution arguments i.e. flags used to run the jobs
22+
this.flags = [];
23+
if (options && options.flags) {
24+
this.flags = this.flags.concat(options.flags);
25+
}
26+
// Holds process.hrtime value
27+
this._time = [0, 0];
28+
// Used to make sure a benchmark only start a timer once
1929
this._started = false;
2030

2131
// this._run will use fork() to create a new process for each configuration
@@ -27,8 +37,8 @@ function Benchmark(fn, options) {
2737
}
2838
}
2939

30-
Benchmark.prototype._parseArgs = function(argv, options) {
31-
const cliOptions = Object.assign({}, options);
40+
Benchmark.prototype._parseArgs = function(argv, configs) {
41+
const cliOptions = Object.assign({}, configs);
3242
const extraOptions = {};
3343
// Parse configuration arguments
3444
for (const arg of argv) {
@@ -38,9 +48,9 @@ Benchmark.prototype._parseArgs = function(argv, options) {
3848
process.exit(1);
3949
}
4050

41-
if (options[match[1]]) {
42-
// Infer the type from the options object and parse accordingly
43-
const isNumber = typeof options[match[1]][0] === 'number';
51+
if (configs[match[1]]) {
52+
// Infer the type from the config object and parse accordingly
53+
const isNumber = typeof configs[match[1]][0] === 'number';
4454
const value = isNumber ? +match[2] : match[2];
4555
cliOptions[match[1]] = [value];
4656
} else {
@@ -138,7 +148,7 @@ Benchmark.prototype._run = function() {
138148

139149
const child = child_process.fork(require.main.filename, childArgs, {
140150
env: childEnv,
141-
execArgv: ['--expose_internals'].concat(process.execArgv)
151+
execArgv: self.flags.concat(process.execArgv)
142152
});
143153
child.on('message', sendResult);
144154
child.on('close', function(code) {

benchmark/misc/freelist.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ var common = require('../common.js');
44

55
var bench = common.createBenchmark(main, {
66
n: [100000]
7+
}, {
8+
flags: ['--expose-internals']
79
});
810

911
function main(conf) {
10-
// Using internal/freelist requires node to be run with --expose_internals
11-
// switch. common.js will do that when calling main(), so we require
12-
// this module here
1312
const FreeList = require('internal/freelist').FreeList;
1413
var n = conf.n;
1514
var poolSize = 1000;

0 commit comments

Comments
 (0)