• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const { fork } = require('child_process');
4const { inspect } = require('util');
5const path = require('path');
6const CLI = require('./_cli.js');
7const BenchmarkProgress = require('./_benchmark_progress.js');
8
9//
10// Parse arguments
11//
12const cli = new CLI(`usage: ./node compare.js [options] [--] <category> ...
13  Run each benchmark in the <category> directory many times using two different
14  node versions. More than one <category> directory can be specified.
15  The output is formatted as csv, which can be processed using for
16  example 'compare.R'.
17
18  --new      ./new-node-binary  new node binary (required)
19  --old      ./old-node-binary  old node binary (required)
20  --runs     30                 number of samples
21  --filter   pattern            includes only benchmark scripts matching
22                                <pattern> (can be repeated)
23  --exclude  pattern            excludes scripts matching <pattern> (can be
24                                repeated)
25  --set      variable=value     set benchmark variable (can be repeated)
26  --no-progress                 don't show benchmark progress indicator
27`, { arrayArgs: ['set', 'filter', 'exclude'], boolArgs: ['no-progress'] });
28
29if (!cli.optional.new || !cli.optional.old) {
30  cli.abort(cli.usage);
31}
32
33const binaries = ['old', 'new'];
34const runs = cli.optional.runs ? parseInt(cli.optional.runs, 10) : 30;
35const benchmarks = cli.benchmarks();
36
37if (benchmarks.length === 0) {
38  console.error('No benchmarks found');
39  process.exitCode = 1;
40  return;
41}
42
43// Create queue from the benchmarks list such both node versions are tested
44// `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.
48const queue = [];
49for (const filename of benchmarks) {
50  for (let iter = 0; iter < runs; iter++) {
51    for (const binary of binaries) {
52      queue.push({ binary, filename, iter });
53    }
54  }
55}
56// queue.length = binary.length * runs * benchmarks.length
57
58// Print csv header
59console.log('"binary","filename","configuration","rate","time"');
60
61const kStartOfQueue = 0;
62
63const showProgress = !cli.optional['no-progress'];
64let progress;
65if (showProgress) {
66  progress = new BenchmarkProgress(queue, benchmarks);
67  progress.startQueue(kStartOfQueue);
68}
69
70(function recursive(i) {
71  const job = queue[i];
72
73  const child = fork(path.resolve(__dirname, job.filename), cli.optional.set, {
74    execPath: cli.optional[job.binary],
75  });
76
77  child.on('message', (data) => {
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}=${inspect(data.conf[key])}`;
83      }
84      conf = conf.slice(1);
85      // Escape quotes (") for correct csv formatting
86      conf = conf.replace(/"/g, '""');
87
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    }
98  });
99
100  child.once('close', (code) => {
101    if (code) {
102      process.exit(code);
103    }
104    if (showProgress) {
105      progress.completeRun(job);
106    }
107
108    // If there are more benchmarks execute the next
109    if (i + 1 < queue.length) {
110      recursive(i + 1);
111    }
112  });
113})(kStartOfQueue);
114