Skip to content

Commit e11f38c

Browse files
BridgeARMylesBorins
authored andcommittedMar 9, 2020
benchmark: refactor helper into a class
This reverts commit 5b0308c. 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 e1347b4 commit e11f38c

File tree

1 file changed

+215
-214
lines changed

1 file changed

+215
-214
lines changed
 

‎benchmark/common.js

+215-214
Original file line numberDiff line numberDiff line change
@@ -3,222 +3,225 @@
33
const child_process = require('child_process');
44
const http_benchmarkers = require('./_http-benchmarkers.js');
55

6-
exports.buildType = process.features.debug ? 'Debug' : 'Release';
7-
8-
exports.createBenchmark = function(fn, configs, options) {
9-
return new Benchmark(fn, configs, options);
10-
};
11-
12-
function Benchmark(fn, configs, options) {
13-
// Use the file name as the name of the benchmark
14-
this.name = require.main.filename.slice(__dirname.length + 1);
15-
// Parse job-specific configuration from the command line arguments
16-
const parsed_args = this._parseArgs(process.argv.slice(2), configs);
17-
this.options = parsed_args.cli;
18-
this.extra_options = parsed_args.extra;
19-
// The configuration list as a queue of jobs
20-
this.queue = this._queue(this.options);
21-
// The configuration of the current job, head of the queue
22-
this.config = this.queue[0];
23-
// Execution arguments i.e. flags used to run the jobs
24-
this.flags = [];
25-
if (options && options.flags) {
26-
this.flags = this.flags.concat(options.flags);
27-
}
28-
if (process.env.NODE_BENCHMARK_FLAGS) {
29-
const flags = process.env.NODE_BENCHMARK_FLAGS.split(/\s+/);
30-
this.flags = this.flags.concat(flags);
31-
}
32-
// Holds process.hrtime value
33-
this._time = [0, 0];
34-
// Used to make sure a benchmark only start a timer once
35-
this._started = false;
36-
this._ended = false;
37-
38-
// this._run will use fork() to create a new process for each configuration
39-
// combination.
40-
if (process.env.hasOwnProperty('NODE_RUN_BENCHMARK_FN')) {
41-
process.nextTick(() => fn(this.config));
42-
} else {
43-
process.nextTick(() => this._run());
44-
}
45-
}
46-
47-
Benchmark.prototype._parseArgs = function(argv, configs) {
48-
const cliOptions = {};
49-
const extraOptions = {};
50-
const validArgRE = /^(.+?)=([\s\S]*)$/;
51-
// Parse configuration arguments
52-
for (const arg of argv) {
53-
const match = arg.match(validArgRE);
54-
if (!match) {
55-
console.error(`bad argument: ${arg}`);
56-
process.exit(1);
6+
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);
10+
// Parse job-specific configuration from the command line arguments
11+
const parsed_args = this._parseArgs(process.argv.slice(2), configs);
12+
this.options = parsed_args.cli;
13+
this.extra_options = parsed_args.extra;
14+
// The configuration list as a queue of jobs
15+
this.queue = this._queue(this.options);
16+
// The configuration of the current job, head of the queue
17+
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);
5722
}
58-
const config = match[1];
59-
60-
if (configs[config]) {
61-
// Infer the type from the config object and parse accordingly
62-
const isNumber = typeof configs[config][0] === 'number';
63-
const value = isNumber ? +match[2] : match[2];
64-
if (!cliOptions[config])
65-
cliOptions[config] = [];
66-
cliOptions[config].push(value);
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));
6737
} else {
68-
extraOptions[config] = match[2];
38+
process.nextTick(() => this._run());
6939
}
7040
}
71-
return { cli: Object.assign({}, configs, cliOptions), extra: extraOptions };
72-
};
73-
74-
Benchmark.prototype._queue = function(options) {
75-
const queue = [];
76-
const keys = Object.keys(options);
77-
78-
// Perform a depth-first walk though all options to generate a
79-
// configuration list that contains all combinations.
80-
function recursive(keyIndex, prevConfig) {
81-
const key = keys[keyIndex];
82-
const values = options[key];
83-
const type = typeof values[0];
8441

85-
for (const value of values) {
86-
if (typeof value !== 'number' && typeof value !== 'string') {
87-
throw new TypeError(`configuration "${key}" had type ${typeof value}`);
42+
_parseArgs(argv, configs) {
43+
const cliOptions = {};
44+
const extraOptions = {};
45+
const validArgRE = /^(.+?)=([\s\S]*)$/;
46+
// Parse configuration arguments
47+
for (const arg of argv) {
48+
const match = arg.match(validArgRE);
49+
if (!match) {
50+
console.error(`bad argument: ${arg}`);
51+
process.exit(1);
8852
}
89-
if (typeof value !== type) {
90-
// This is a requirement for being able to consistently and predictably
91-
// parse CLI provided configuration values.
92-
throw new TypeError(`configuration "${key}" has mixed types`);
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);
62+
} else {
63+
extraOptions[config] = match[2];
9364
}
65+
}
66+
return { cli: Object.assign({}, configs, cliOptions), extra: extraOptions };
67+
}
9468

95-
const currConfig = Object.assign({ [key]: value }, prevConfig);
96-
97-
if (keyIndex + 1 < keys.length) {
98-
recursive(keyIndex + 1, currConfig);
99-
} else {
100-
queue.push(currConfig);
69+
_queue(options) {
70+
const queue = [];
71+
const keys = Object.keys(options);
72+
73+
// Perform a depth-first walk though all options to generate a
74+
// configuration list that contains all combinations.
75+
function recursive(keyIndex, prevConfig) {
76+
const key = keys[keyIndex];
77+
const values = options[key];
78+
const type = typeof values[0];
79+
80+
for (const value of values) {
81+
if (typeof value !== 'number' && typeof value !== 'string') {
82+
throw new TypeError(
83+
`configuration "${key}" had type ${typeof value}`);
84+
}
85+
if (typeof value !== type) {
86+
// This is a requirement for being able to consistently and
87+
// predictably parse CLI provided configuration values.
88+
throw new TypeError(`configuration "${key}" has mixed types`);
89+
}
90+
91+
const currConfig = Object.assign({ [key]: value }, prevConfig);
92+
93+
if (keyIndex + 1 < keys.length) {
94+
recursive(keyIndex + 1, currConfig);
95+
} else {
96+
queue.push(currConfig);
97+
}
10198
}
10299
}
100+
101+
if (keys.length > 0) {
102+
recursive(0, {});
103+
} else {
104+
queue.push({});
105+
}
106+
107+
return queue;
103108
}
104109

105-
if (keys.length > 0) {
106-
recursive(0, {});
107-
} else {
108-
queue.push({});
110+
http(options, cb) {
111+
const self = this;
112+
const http_options = Object.assign({ }, options);
113+
http_options.benchmarker = http_options.benchmarker ||
114+
self.config.benchmarker ||
115+
self.extra_options.benchmarker ||
116+
exports.default_http_benchmarker;
117+
http_benchmarkers.run(
118+
http_options, (error, code, used_benchmarker, result, elapsed) => {
119+
if (cb) {
120+
cb(code);
121+
}
122+
if (error) {
123+
console.error(error);
124+
process.exit(code || 1);
125+
}
126+
self.config.benchmarker = used_benchmarker;
127+
self.report(result, elapsed);
128+
}
129+
);
109130
}
110131

111-
return queue;
112-
};
132+
_run() {
133+
const self = this;
134+
// If forked, report to the parent.
135+
if (process.send) {
136+
process.send({
137+
type: 'config',
138+
name: this.name,
139+
queueLength: this.queue.length,
140+
});
141+
}
142+
143+
(function recursive(queueIndex) {
144+
const config = self.queue[queueIndex];
113145

114-
// Benchmark an http server.
115-
exports.default_http_benchmarker =
116-
http_benchmarkers.default_http_benchmarker;
117-
exports.PORT = http_benchmarkers.PORT;
118-
119-
Benchmark.prototype.http = function(options, cb) {
120-
const self = this;
121-
const http_options = Object.assign({ }, options);
122-
http_options.benchmarker = http_options.benchmarker ||
123-
self.config.benchmarker ||
124-
self.extra_options.benchmarker ||
125-
exports.default_http_benchmarker;
126-
http_benchmarkers.run(
127-
http_options, (error, code, used_benchmarker, result, elapsed) => {
128-
if (cb) {
129-
cb(code);
146+
// Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't
147+
// construct a configuration queue, but just execute the benchmark
148+
// function.
149+
const childEnv = Object.assign({}, process.env);
150+
childEnv.NODE_RUN_BENCHMARK_FN = '';
151+
152+
// Create configuration arguments
153+
const childArgs = [];
154+
for (const key of Object.keys(config)) {
155+
childArgs.push(`${key}=${config[key]}`);
130156
}
131-
if (error) {
132-
console.error(error);
133-
process.exit(code || 1);
157+
for (const key of Object.keys(self.extra_options)) {
158+
childArgs.push(`${key}=${self.extra_options[key]}`);
134159
}
135-
self.config.benchmarker = used_benchmarker;
136-
self.report(result, elapsed);
137-
}
138-
);
139-
};
140160

141-
Benchmark.prototype._run = function() {
142-
const self = this;
143-
// If forked, report to the parent.
144-
if (process.send) {
145-
process.send({
146-
type: 'config',
147-
name: this.name,
148-
queueLength: this.queue.length,
149-
});
161+
const child = child_process.fork(require.main.filename, childArgs, {
162+
env: childEnv,
163+
execArgv: self.flags.concat(process.execArgv),
164+
});
165+
child.on('message', sendResult);
166+
child.on('close', (code) => {
167+
if (code) {
168+
process.exit(code);
169+
}
170+
171+
if (queueIndex + 1 < self.queue.length) {
172+
recursive(queueIndex + 1);
173+
}
174+
});
175+
})(0);
150176
}
151177

152-
(function recursive(queueIndex) {
153-
const config = self.queue[queueIndex];
178+
start() {
179+
if (this._started) {
180+
throw new Error('Called start more than once in a single benchmark');
181+
}
182+
this._started = true;
183+
this._time = process.hrtime();
184+
}
154185

155-
// Set NODE_RUN_BENCHMARK_FN to indicate that the child shouldn't construct
156-
// a configuration queue, but just execute the benchmark function.
157-
const childEnv = Object.assign({}, process.env);
158-
childEnv.NODE_RUN_BENCHMARK_FN = '';
186+
end(operations) {
187+
// Get elapsed time now and do error checking later for accuracy.
188+
const elapsed = process.hrtime(this._time);
159189

160-
// Create configuration arguments
161-
const childArgs = [];
162-
for (const key of Object.keys(config)) {
163-
childArgs.push(`${key}=${config[key]}`);
190+
if (!this._started) {
191+
throw new Error('called end without start');
164192
}
165-
for (const key of Object.keys(self.extra_options)) {
166-
childArgs.push(`${key}=${self.extra_options[key]}`);
193+
if (this._ended) {
194+
throw new Error('called end multiple times');
195+
}
196+
if (typeof operations !== 'number') {
197+
throw new Error('called end() without specifying operation count');
198+
}
199+
if (!process.env.NODEJS_BENCHMARK_ZERO_ALLOWED && operations <= 0) {
200+
throw new Error('called end() with operation count <= 0');
201+
}
202+
if (elapsed[0] === 0 && elapsed[1] === 0) {
203+
if (!process.env.NODEJS_BENCHMARK_ZERO_ALLOWED)
204+
throw new Error('insufficient clock precision for short benchmark');
205+
// Avoid dividing by zero
206+
elapsed[1] = 1;
167207
}
168208

169-
const child = child_process.fork(require.main.filename, childArgs, {
170-
env: childEnv,
171-
execArgv: self.flags.concat(process.execArgv),
172-
});
173-
child.on('message', sendResult);
174-
child.on('close', (code) => {
175-
if (code) {
176-
process.exit(code);
177-
}
178-
179-
if (queueIndex + 1 < self.queue.length) {
180-
recursive(queueIndex + 1);
181-
}
182-
});
183-
})(0);
184-
};
185-
186-
Benchmark.prototype.start = function() {
187-
if (this._started) {
188-
throw new Error('Called start more than once in a single benchmark');
209+
this._ended = true;
210+
const time = elapsed[0] + elapsed[1] / 1e9;
211+
const rate = operations / time;
212+
this.report(rate, elapsed);
189213
}
190-
this._started = true;
191-
this._time = process.hrtime();
192-
};
193214

194-
Benchmark.prototype.end = function(operations) {
195-
// Get elapsed time now and do error checking later for accuracy.
196-
const elapsed = process.hrtime(this._time);
197-
198-
if (!this._started) {
199-
throw new Error('called end without start');
200-
}
201-
if (this._ended) {
202-
throw new Error('called end multiple times');
203-
}
204-
if (typeof operations !== 'number') {
205-
throw new Error('called end() without specifying operation count');
206-
}
207-
if (!process.env.NODEJS_BENCHMARK_ZERO_ALLOWED && operations <= 0) {
208-
throw new Error('called end() with operation count <= 0');
209-
}
210-
if (elapsed[0] === 0 && elapsed[1] === 0) {
211-
if (!process.env.NODEJS_BENCHMARK_ZERO_ALLOWED)
212-
throw new Error('insufficient clock precision for short benchmark');
213-
// Avoid dividing by zero
214-
elapsed[1] = 1;
215+
report(rate, elapsed) {
216+
sendResult({
217+
name: this.name,
218+
conf: this.config,
219+
rate: rate,
220+
time: elapsed[0] + elapsed[1] / 1e9,
221+
type: 'report',
222+
});
215223
}
216-
217-
this._ended = true;
218-
const time = elapsed[0] + elapsed[1] / 1e9;
219-
const rate = operations / time;
220-
this.report(rate, elapsed);
221-
};
224+
}
222225

223226
function formatResult(data) {
224227
// Construct configuration string, " A=a, B=b, ..."
@@ -242,27 +245,6 @@ function sendResult(data) {
242245
console.log(formatResult(data));
243246
}
244247
}
245-
exports.sendResult = sendResult;
246-
247-
Benchmark.prototype.report = function(rate, elapsed) {
248-
sendResult({
249-
name: this.name,
250-
conf: this.config,
251-
rate: rate,
252-
time: elapsed[0] + elapsed[1] / 1e9,
253-
type: 'report',
254-
});
255-
};
256-
257-
exports.binding = function(bindingName) {
258-
try {
259-
const { internalBinding } = require('internal/test/binding');
260-
261-
return internalBinding(bindingName);
262-
} catch {
263-
return process.binding(bindingName);
264-
}
265-
};
266248

267249
const urls = {
268250
long: 'http://nodejs.org:89/docs/latest/api/foo/bar/qua/13949281/0f28b/' +
@@ -278,7 +260,6 @@ const urls = {
278260
percent: 'https://%E4%BD%A0/foo',
279261
dot: 'https://example.org/./a/../b/./c',
280262
};
281-
exports.urls = urls;
282263

283264
const searchParams = {
284265
noencode: 'foo=bar&baz=quux&xyzzy=thud',
@@ -293,7 +274,6 @@ const searchParams = {
293274
manyblankpairs: '&&&&&&&&&&&&&&&&&&&&&&&&',
294275
altspaces: 'foo+bar=baz+quux&xyzzy+thud=quuy+quuz&abc=def+ghi',
295276
};
296-
exports.searchParams = searchParams;
297277

298278
function getUrlData(withBase) {
299279
const data = require('../test/fixtures/wpt/url/resources/urltestdata.json');
@@ -309,8 +289,6 @@ function getUrlData(withBase) {
309289
return result;
310290
}
311291

312-
exports.urlDataTypes = Object.keys(urls).concat(['wpt']);
313-
314292
/**
315293
* Generate an array of data for URL benchmarks to use.
316294
* The size of the resulting data set is the original data size * 2 ** `e`.
@@ -354,4 +332,27 @@ function bakeUrlData(type, e = 0, withBase = false, asUrl = false) {
354332
}
355333
return result;
356334
}
357-
exports.bakeUrlData = bakeUrlData;
335+
336+
module.exports = {
337+
PORT: http_benchmarkers.PORT,
338+
bakeUrlData,
339+
binding(bindingName) {
340+
try {
341+
const { internalBinding } = require('internal/test/binding');
342+
343+
return internalBinding(bindingName);
344+
} catch {
345+
return process.binding(bindingName);
346+
}
347+
},
348+
buildType: process.features.debug ? 'Debug' : 'Release',
349+
createBenchmark(fn, configs, options) {
350+
return new Benchmark(fn, configs, options);
351+
},
352+
// Benchmark an http server.
353+
default_http_benchmarker: http_benchmarkers.default_http_benchmarker,
354+
sendResult,
355+
searchParams,
356+
urlDataTypes: Object.keys(urls).concat(['wpt']),
357+
urls,
358+
};

0 commit comments

Comments
 (0)
Please sign in to comment.