main
js 112 lines 3.38 KB
Raw
1 'use strict';
2
3 const chalk = require('chalk');
4 const Table = require('cli-table');
5
6 function percentChange(prev, current, prevSem, currentSem) {
7 const [mean, sd] = calculateMeanAndSdOfRatioFromDeltaMethod(
8 prev,
9 current,
10 prevSem,
11 currentSem
12 );
13 const pctChange = +(mean * 100).toFixed(1);
14 const ci95 = +(100 * 1.96 * sd).toFixed(1);
15
16 const ciInfo = ci95 > 0 ? ` +- ${ci95} %` : '';
17 const text = `${pctChange > 0 ? '+' : ''}${pctChange} %${ciInfo}`;
18 if (pctChange + ci95 < 0) {
19 return chalk.green(text);
20 } else if (pctChange - ci95 > 0) {
21 return chalk.red(text);
22 } else {
23 // Statistically insignificant.
24 return text;
25 }
26 }
27
28 function calculateMeanAndSdOfRatioFromDeltaMethod(
29 meanControl,
30 meanTest,
31 semControl,
32 semTest
33 ) {
34 const mean =
35 (meanTest - meanControl) / meanControl -
36 (Math.pow(semControl, 2) * meanTest) / Math.pow(meanControl, 3);
37 const variance =
38 Math.pow(semTest / meanControl, 2) +
39 Math.pow(semControl * meanTest, 2) / Math.pow(meanControl, 4);
40 return [mean, Math.sqrt(variance)];
41 }
42
43 function addBenchmarkResults(table, localResults, remoteMasterResults) {
44 const benchmarks = Object.keys(
45 (localResults && localResults.benchmarks) ||
46 (remoteMasterResults && remoteMasterResults.benchmarks)
47 );
48 benchmarks.forEach(benchmark => {
49 const rowHeader = [chalk.white.bold(benchmark)];
50 if (remoteMasterResults) {
51 rowHeader.push(chalk.white.bold('Time'));
52 }
53 if (localResults) {
54 rowHeader.push(chalk.white.bold('Time'));
55 }
56 if (localResults && remoteMasterResults) {
57 rowHeader.push(chalk.white.bold('Diff'));
58 }
59 table.push(rowHeader);
60
61 const measurements =
62 (localResults && localResults.benchmarks[benchmark].averages) ||
63 (remoteMasterResults &&
64 remoteMasterResults.benchmarks[benchmark].averages);
65 measurements.forEach((measurement, i) => {
66 const row = [chalk.gray(measurement.entry)];
67 let remoteMean;
68 let remoteSem;
69 if (remoteMasterResults) {
70 remoteMean = remoteMasterResults.benchmarks[benchmark].averages[i].mean;
71 remoteSem = remoteMasterResults.benchmarks[benchmark].averages[i].sem;
72 // https://en.wikipedia.org/wiki/1.96 gives a 99% confidence interval.
73 const ci95 = remoteSem * 1.96;
74 row.push(
75 chalk.white(+remoteMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2))
76 );
77 }
78 let localMean;
79 let localSem;
80 if (localResults) {
81 localMean = localResults.benchmarks[benchmark].averages[i].mean;
82 localSem = localResults.benchmarks[benchmark].averages[i].sem;
83 const ci95 = localSem * 1.96;
84 row.push(
85 chalk.white(+localMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2))
86 );
87 }
88 if (localResults && remoteMasterResults) {
89 row.push(percentChange(remoteMean, localMean, remoteSem, localSem));
90 }
91 table.push(row);
92 });
93 });
94 }
95
96 function printResults(localResults, remoteMasterResults) {
97 const head = [''];
98 if (remoteMasterResults) {
99 head.push(chalk.yellow.bold('Remote (Merge Base)'));
100 }
101 if (localResults) {
102 head.push(chalk.green.bold('Local (Current Branch)'));
103 }
104 if (localResults && remoteMasterResults) {
105 head.push('');
106 }
107 const table = new Table({head});
108 addBenchmarkResults(table, localResults, remoteMasterResults);
109 console.log(table.toString());
110 }
111
112 module.exports = printResults;