Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f61c71b

Browse filesBrowse files
joyeecheungevanlucas
authored andcommitted
benchmark: add progress indicator to compare.js
* Print the progress bar and the current benchmark to stderr when stderr is TTY and stdout is not. * Allow cli arguments without values via setting.boolArgs * Add --no-progress option PR-URL: #10823 Fixes: #8659 Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
1 parent c6af766 commit f61c71b
Copy full SHA for f61c71b

File tree

Expand file treeCollapse file tree

6 files changed

+187
-24
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+187
-24
lines changed
Open diff view settings
Collapse file

‎benchmark/_benchmark_progress.js‎

Copy file name to clipboard
+120Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
'use strict';
2+
3+
const readline = require('readline');
4+
5+
function pad(input, minLength, fill) {
6+
var result = input + '';
7+
return fill.repeat(Math.max(0, minLength - result.length)) + result;
8+
}
9+
10+
function fraction(numerator, denominator) {
11+
const fdenominator = denominator + '';
12+
const fnumerator = pad(numerator, fdenominator.length, ' ');
13+
return `${fnumerator}/${fdenominator}`;
14+
}
15+
16+
function getTime(diff) {
17+
const time = Math.ceil(diff[0] + diff[1] / 1e9);
18+
const seconds = pad(time % 60, 2, '0');
19+
const minutes = pad(Math.floor(time / 60) % (60 * 60), 2, '0');
20+
const hours = pad(Math.floor(time / (60 * 60)), 2, '0');
21+
return `${hours}:${minutes}:${seconds}`;
22+
}
23+
24+
// A run is an item in the job queue: { binary, filename, iter }
25+
// A config is an item in the subqueue: { binary, filename, iter, configs }
26+
class BenchmarkProgress {
27+
constructor(queue, benchmarks) {
28+
this.queue = queue; // Scheduled runs.
29+
this.benchmarks = benchmarks; // Filenames of scheduled benchmarks.
30+
this.completedRuns = 0; // Number of completed runs.
31+
this.scheduledRuns = queue.length; // Number of scheduled runs.
32+
// Time when starting to run benchmarks.
33+
this.startTime = process.hrtime();
34+
// Number of times each file will be run (roughly).
35+
this.runsPerFile = queue.length / benchmarks.length;
36+
this.currentFile = ''; // Filename of current benchmark.
37+
this.currentFileConfig; // Configurations for current file
38+
// Number of configurations already run for the current file.
39+
this.completedConfig = 0;
40+
// Total number of configurations for the current file
41+
this.scheduledConfig = 0;
42+
this.interval = 0; // result of setInterval for updating the elapsed time
43+
}
44+
45+
startQueue(index) {
46+
this.kStartOfQueue = index;
47+
this.currentFile = this.queue[index].filename;
48+
this.interval = setInterval(() => {
49+
if (this.completedRuns === this.scheduledRuns) {
50+
clearInterval(this.interval);
51+
} else {
52+
this.updateProgress();
53+
}
54+
}, 1000);
55+
}
56+
57+
startSubqueue(data, index) {
58+
// This subqueue is generated by a new benchmark
59+
if (data.name !== this.currentFile || index === this.kStartOfQueue) {
60+
this.currentFile = data.name;
61+
this.scheduledConfig = data.queueLength;
62+
}
63+
this.completedConfig = 0;
64+
this.updateProgress();
65+
}
66+
67+
completeConfig(data) {
68+
this.completedConfig++;
69+
this.updateProgress();
70+
}
71+
72+
completeRun(job) {
73+
this.completedRuns++;
74+
this.updateProgress();
75+
}
76+
77+
getProgress() {
78+
// Get time as soon as possible.
79+
const diff = process.hrtime(this.startTime);
80+
81+
const completedRuns = this.completedRuns;
82+
const scheduledRuns = this.scheduledRuns;
83+
const finished = completedRuns === scheduledRuns;
84+
85+
// Calculate numbers for fractions.
86+
const runsPerFile = this.runsPerFile;
87+
const completedFiles = Math.floor(completedRuns / runsPerFile);
88+
const scheduledFiles = this.benchmarks.length;
89+
const completedRunsForFile = finished ? runsPerFile :
90+
completedRuns % runsPerFile;
91+
const completedConfig = this.completedConfig;
92+
const scheduledConfig = this.scheduledConfig;
93+
94+
// Calculate the percentage.
95+
let runRate = 0; // Rate of current incomplete run.
96+
if (completedConfig !== scheduledConfig) {
97+
runRate = completedConfig / scheduledConfig;
98+
}
99+
const completedRate = ((completedRuns + runRate) / scheduledRuns);
100+
const percent = pad(Math.floor(completedRate * 100), 3, ' ');
101+
102+
const caption = finished ? 'Done\n' : this.currentFile;
103+
return `[${getTime(diff)}|% ${percent}` +
104+
`| ${fraction(completedFiles, scheduledFiles)} files ` +
105+
`| ${fraction(completedRunsForFile, runsPerFile)} runs ` +
106+
`| ${fraction(completedConfig, scheduledConfig)} configs]` +
107+
`: ${caption}`;
108+
}
109+
110+
updateProgress(finished) {
111+
if (!process.stderr.isTTY || process.stdout.isTTY) {
112+
return;
113+
}
114+
readline.clearLine(process.stderr);
115+
readline.cursorTo(process.stderr, 0);
116+
process.stderr.write(this.getProgress());
117+
}
118+
}
119+
120+
module.exports = BenchmarkProgress;
Collapse file

‎benchmark/_cli.js‎

Copy file name to clipboardExpand all lines: benchmark/_cli.js
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ function CLI(usage, settings) {
4545
currentOptional = arg.slice(1);
4646
}
4747

48-
// Default the value to true
49-
if (!settings.arrayArgs.includes(currentOptional)) {
48+
if (settings.boolArgs && settings.boolArgs.includes(currentOptional)) {
5049
this.optional[currentOptional] = true;
50+
mode = 'both';
51+
} else {
52+
// expect the next value to be option related (either -- or the value)
53+
mode = 'option';
5154
}
52-
53-
// expect the next value to be option related (either -- or the value)
54-
mode = 'option';
5555
} else if (mode === 'option') {
5656
// Optional arguments value
5757

Collapse file

‎benchmark/common.js‎

Copy file name to clipboardExpand all lines: benchmark/common.js
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ Benchmark.prototype.http = function(options, cb) {
128128

129129
Benchmark.prototype._run = function() {
130130
const self = this;
131+
// If forked, report to the parent.
132+
if (process.send) {
133+
process.send({
134+
type: 'config',
135+
name: this.name,
136+
queueLength: this.queue.length
137+
});
138+
}
131139

132140
(function recursive(queueIndex) {
133141
const config = self.queue[queueIndex];
@@ -217,7 +225,8 @@ Benchmark.prototype.report = function(rate, elapsed) {
217225
name: this.name,
218226
conf: this.config,
219227
rate: rate,
220-
time: elapsed[0] + elapsed[1] / 1e9
228+
time: elapsed[0] + elapsed[1] / 1e9,
229+
type: 'report'
221230
});
222231
};
223232

Collapse file

‎benchmark/compare.js‎

Copy file name to clipboardExpand all lines: benchmark/compare.js
+45-18Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const fork = require('child_process').fork;
44
const path = require('path');
55
const CLI = require('./_cli.js');
6+
const BenchmarkProgress = require('./_benchmark_progress.js');
67

78
//
89
// Parse arguments
@@ -13,13 +14,15 @@ const cli = CLI(`usage: ./node compare.js [options] [--] <category> ...
1314
The output is formatted as csv, which can be processed using for
1415
example 'compare.R'.
1516
16-
--new ./new-node-binary new node binary (required)
17-
--old ./old-node-binary old node binary (required)
18-
--runs 30 number of samples
19-
--filter pattern string to filter benchmark scripts
20-
--set variable=value set benchmark variable (can be repeated)
17+
--new ./new-node-binary new node binary (required)
18+
--old ./old-node-binary old node binary (required)
19+
--runs 30 number of samples
20+
--filter pattern string to filter benchmark scripts
21+
--set variable=value set benchmark variable (can be repeated)
22+
--no-progress don't show benchmark progress indicator
2123
`, {
22-
arrayArgs: ['set']
24+
arrayArgs: ['set'],
25+
boolArgs: ['no-progress']
2326
});
2427

2528
if (!cli.optional.new || !cli.optional.old) {
@@ -39,6 +42,9 @@ if (benchmarks.length === 0) {
3942

4043
// Create queue from the benchmarks list such both node versions are tested
4144
// `runs` amount of times each.
45+
// Note: BenchmarkProgress relies on this order to estimate
46+
// how much runs remaining for a file. All benchmarks generated from
47+
// the same file must be run consecutively.
4248
const queue = [];
4349
for (const filename of benchmarks) {
4450
for (let iter = 0; iter < runs; iter++) {
@@ -47,10 +53,20 @@ for (const filename of benchmarks) {
4753
}
4854
}
4955
}
56+
// queue.length = binary.length * runs * benchmarks.length
5057

5158
// Print csv header
5259
console.log('"binary", "filename", "configuration", "rate", "time"');
5360

61+
const kStartOfQueue = 0;
62+
63+
const showProgress = !cli.optional['no-progress'];
64+
let progress;
65+
if (showProgress) {
66+
progress = new BenchmarkProgress(queue, benchmarks);
67+
progress.startQueue(kStartOfQueue);
68+
}
69+
5470
(function recursive(i) {
5571
const job = queue[i];
5672

@@ -59,29 +75,40 @@ console.log('"binary", "filename", "configuration", "rate", "time"');
5975
});
6076

6177
child.on('message', function(data) {
62-
// Construct configuration string, " A=a, B=b, ..."
63-
let conf = '';
64-
for (const key of Object.keys(data.conf)) {
65-
conf += ' ' + key + '=' + JSON.stringify(data.conf[key]);
66-
}
67-
conf = conf.slice(1);
78+
if (data.type === 'report') {
79+
// Construct configuration string, " A=a, B=b, ..."
80+
let conf = '';
81+
for (const key of Object.keys(data.conf)) {
82+
conf += ' ' + key + '=' + JSON.stringify(data.conf[key]);
83+
}
84+
conf = conf.slice(1);
85+
// Escape quotes (") for correct csv formatting
86+
conf = conf.replace(/"/g, '""');
6887

69-
// Escape quotes (") for correct csv formatting
70-
conf = conf.replace(/"/g, '""');
71-
72-
console.log(`"${job.binary}", "${job.filename}", "${conf}", ` +
73-
`${data.rate}, ${data.time}`);
88+
console.log(`"${job.binary}", "${job.filename}", "${conf}", ` +
89+
`${data.rate}, ${data.time}`);
90+
if (showProgress) {
91+
// One item in the subqueue has been completed.
92+
progress.completeConfig(data);
93+
}
94+
} else if (showProgress && data.type === 'config') {
95+
// The child has computed the configurations, ready to run subqueue.
96+
progress.startSubqueue(data, i);
97+
}
7498
});
7599

76100
child.once('close', function(code) {
77101
if (code) {
78102
process.exit(code);
79103
return;
80104
}
105+
if (showProgress) {
106+
progress.completeRun(job);
107+
}
81108

82109
// If there are more benchmarks execute the next
83110
if (i + 1 < queue.length) {
84111
recursive(i + 1);
85112
}
86113
});
87-
})(0);
114+
})(kStartOfQueue);
Collapse file

‎benchmark/run.js‎

Copy file name to clipboardExpand all lines: benchmark/run.js
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ if (format === 'csv') {
4444
}
4545

4646
child.on('message', function(data) {
47+
if (data.type !== 'report') {
48+
return;
49+
}
4750
// Construct configuration string, " A=a, B=b, ..."
4851
let conf = '';
4952
for (const key of Object.keys(data.conf)) {
Collapse file

‎benchmark/scatter.js‎

Copy file name to clipboardExpand all lines: benchmark/scatter.js
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ function csvEncodeValue(value) {
4242
const child = fork(path.resolve(__dirname, filepath), cli.optional.set);
4343

4444
child.on('message', function(data) {
45+
if (data.type !== 'report') {
46+
return;
47+
}
48+
4549
// print csv header
4650
if (printHeader) {
4751
const confHeader = Object.keys(data.conf)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.