pdf.js/test/stats/statcmp.js
Jonas Jenwald de36b2aaba Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).

Prettier is being used for a couple of reasons:

 - To be consistent with `mozilla-central`, where Prettier is already in use across the tree.

 - To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.

Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.

*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.

(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
2019-12-26 12:34:24 +01:00

213 lines
5.7 KiB
JavaScript

"use strict";
var fs = require("fs");
try {
var ttest = require("ttest");
} catch (e) {
console.log('\nttest is not installed -- to intall, run "npm install ttest"');
console.log("Continuing without significance test...\n");
}
var VALID_GROUP_BYS = ["browser", "pdf", "page", "round", "stat"];
function parseOptions() {
var yargs = require("yargs")
.usage(
"Compare the results of two stats files.\n" +
"Usage:\n $0 <BASELINE> <CURRENT> [options]"
)
.demand(2)
.string(["groupBy"])
.describe(
"groupBy",
"How statistics should grouped. Valid options: " +
VALID_GROUP_BYS.join(" ")
)
.default("groupBy", "browser,stat");
var result = yargs.argv;
result.baseline = result._[0];
result.current = result._[1];
if (result.groupBy) {
result.groupBy = result.groupBy.split(/[;, ]+/);
}
return result;
}
function group(stats, groupBy) {
var vals = [];
for (var i = 0; i < stats.length; i++) {
var stat = stats[i];
var keyArr = [];
for (var j = 0; j < groupBy.length; j++) {
keyArr.push(stat[groupBy[j]]);
}
var key = keyArr.join(",");
if (vals[key] === undefined) {
vals[key] = [];
}
vals[key].push(stat["time"]);
}
return vals;
}
/*
* Flatten the stats so that there's one row per stats entry.
* Also, if results are not grouped by 'stat', keep only 'Overall' results.
*/
function flatten(stats) {
var rows = [];
stats.forEach(function(stat) {
stat["stats"].forEach(function(s) {
rows.push({
browser: stat["browser"],
page: stat["page"],
pdf: stat["pdf"],
round: stat["round"],
stat: s["name"],
time: s["end"] - s["start"],
});
});
});
// Use only overall results if not grouped by 'stat'
if (!options.groupBy.includes("stat")) {
rows = rows.filter(function(s) {
return s.stat === "Overall";
});
}
return rows;
}
function pad(s, length, dir /* default: 'right' */) {
s = "" + s;
var spaces = new Array(Math.max(0, length - s.length + 1)).join(" ");
return dir === "left" ? spaces + s : s + spaces;
}
function mean(array) {
function add(a, b) {
return a + b;
}
return array.reduce(add, 0) / array.length;
}
/* Comparator for row key sorting. */
function compareRow(a, b) {
a = a.split(",");
b = b.split(",");
for (var i = 0; i < Math.min(a.length, b.length); i++) {
var intA = parseInt(a[i], 10);
var intB = parseInt(b[i], 10);
var ai = isNaN(intA) ? a[i] : intA;
var bi = isNaN(intB) ? b[i] : intB;
if (ai < bi) {
return -1;
}
if (ai > bi) {
return 1;
}
}
return 0;
}
/*
* Dump various stats in a table to compare the baseline and current results.
* T-test Refresher:
* If I understand t-test correctly, p is the probability that we'll observe
* another test that is as extreme as the current result assuming the null
* hypothesis is true. P is NOT the probability of the null hypothesis. The null
* hypothesis in this case is that the baseline and current results will be the
* same. It is generally accepted that you can reject the null hypothesis if the
* p-value is less than 0.05. So if p < 0.05 we can reject the results are the
* same which doesn't necessarily mean the results are faster/slower but it can
* be implied.
*/
function stat(baseline, current) {
var baselineGroup = group(baseline, options.groupBy);
var currentGroup = group(current, options.groupBy);
var keys = Object.keys(baselineGroup);
keys.sort(compareRow);
var labels = options.groupBy.slice(0);
labels.push("Count", "Baseline(ms)", "Current(ms)", "+/-", "% ");
if (ttest) {
labels.push("Result(P<.05)");
}
var i,
row,
rows = [];
// collect rows and measure column widths
var width = labels.map(function(s) {
return s.length;
});
rows.push(labels);
for (var k = 0; k < keys.length; k++) {
var key = keys[k];
var baselineMean = mean(baselineGroup[key]);
var currentMean = mean(currentGroup[key]);
row = key.split(",");
row.push(
"" + baselineGroup[key].length,
"" + Math.round(baselineMean),
"" + Math.round(currentMean),
"" + Math.round(currentMean - baselineMean),
((100 * (currentMean - baselineMean)) / baselineMean).toFixed(2)
);
if (ttest) {
var p =
baselineGroup[key].length < 2
? 1
: ttest(baselineGroup[key], currentGroup[key]).pValue();
if (p < 0.05) {
row.push(currentMean < baselineMean ? "faster" : "slower");
} else {
row.push("");
}
}
for (i = 0; i < row.length; i++) {
width[i] = Math.max(width[i], row[i].length);
}
rows.push(row);
}
// add horizontal line
var hline = width.map(function(w) {
return new Array(w + 1).join("-");
});
rows.splice(1, 0, hline);
// print output
console.log("-- Grouped By " + options.groupBy.join(", ") + " --");
var groupCount = options.groupBy.length;
for (var r = 0; r < rows.length; r++) {
row = rows[r];
for (i = 0; i < row.length; i++) {
row[i] = pad(row[i], width[i], i < groupCount ? "right" : "left");
}
console.log(row.join(" | "));
}
}
function main() {
var baseline, current;
try {
var baselineFile = fs.readFileSync(options.baseline).toString();
baseline = flatten(JSON.parse(baselineFile));
} catch (e) {
console.log('Error reading file "' + options.baseline + '": ' + e);
process.exit(0);
}
try {
var currentFile = fs.readFileSync(options.current).toString();
current = flatten(JSON.parse(currentFile));
} catch (e) {
console.log('Error reading file "' + options.current + '": ' + e);
process.exit(0);
}
stat(baseline, current);
}
var options = parseOptions();
main();