Files
react/scripts/bench/stats.js
Dominic Gannaway a7d8ebd2b5 Add React benchmarking infrastructure (#9465)
* Initial commit for WIP benchmarking infrastructure

* fixed lint issues and ran prettier

* added <rootDir>/scripts/bench/ to ignore paths for Jest

* tidied up code and fixed a few bugs in the runner.js

* fixed eslint

* improved the benchmark output from the runner

* fixed typo

* tided up print output in runner.js

* throw error if chrome canary is not installed on mac

* added better bench stats output (tables)

* added benchmark diff to table results

* adds bundle size comparisons to results

* tidied up the results

* fixed prettier output

* attempt to trigger bech for circleci build

* fixes flow exlclusion for lighthouse module

* added class components benchmark

* cleaned up stats.js

* stability changes

* circleci node version to 7

* added another benchmark

* added colours to the different benchmarks to check if being cached

* force no-cache headers

* added more info messages

* refactor chrome launching.

* fixed an issue where launcher.kill might fail

* Move server to runner. Launch it only once.

* tidy up

* changes the logic in how the remote repo is checked out

* removes bench from circleci build

* removed colors from benchmarks (no longer needed)

* added CI integration comment

* added hacker news benchmark

* added skipBuild functionality

* relabelled remote

* Add confidence intervals

* added first meaningful paint

* removed some unused code

* reverted code.json

* updated benchmark runs back to 10

* no longer breaks when results contain missing bundles

* adds CPU throttling

* renamed build to remote-repo

* small fix to build

* fixed bad merge

* upped runs to 10 from 2 again

* properly pulls master

* removes old-bench

* runs benchmarks in headless mode

* adds a --headless option

* improved the git build process

* added README

* updated based feedback from review

* adds merge base commit sha

* addressing more PR feedback

* remove built JS react files

* updated .gitignore

* added combined bundle load times to the metrics
2017-05-09 17:13:54 +01:00

164 lines
4.8 KiB
JavaScript

'use strict';
const chalk = require('chalk');
const Table = require('cli-table');
function percentChange(prev, current, prevSem, currentSem) {
const [mean, sd] = calculateMeanAndSdOfRatioFromDeltaMethod(
prev,
current,
prevSem,
currentSem
);
const pctChange = +(mean * 100).toFixed(1);
const ci95 = +(100 * 1.96 * sd).toFixed(1);
const ciInfo = ci95 > 0 ? ` +- ${ci95} %` : '';
const text = `${pctChange > 0 ? '+' : ''}${pctChange} %${ciInfo}`;
if (pctChange + ci95 < 0) {
return chalk.green(text);
} else if (pctChange - ci95 > 0) {
return chalk.red(text);
} else {
// Statistically insignificant.
return text;
}
}
function calculateMeanAndSdOfRatioFromDeltaMethod(
meanControl,
meanTest,
semControl,
semTest
) {
const mean = (
((meanTest - meanControl) / meanControl) -
(Math.pow(semControl, 2) * meanTest / Math.pow(meanControl, 3))
);
const variance = (
Math.pow(semTest / meanControl, 2) +
(Math.pow(semControl * meanTest, 2) / Math.pow(meanControl, 4))
);
return [mean, Math.sqrt(variance)];
}
function addBenchmarkResults(table, localResults, remoteMasterResults) {
const benchmarks = Object.keys(
(localResults && localResults.benchmarks) || (remoteMasterResults && remoteMasterResults.benchmarks)
);
benchmarks.forEach(benchmark => {
const rowHeader = [chalk.white.bold(benchmark)];
if (remoteMasterResults) {
rowHeader.push(chalk.white.bold('Time'));
}
if (localResults) {
rowHeader.push(chalk.white.bold('Time'));
}
if (localResults && remoteMasterResults) {
rowHeader.push(chalk.white.bold('Diff'));
}
table.push(rowHeader);
const measurements = (
(localResults && localResults.benchmarks[benchmark].averages)
||
(remoteMasterResults && remoteMasterResults.benchmarks[benchmark].averages)
);
measurements.forEach((measurement, i) => {
const row = [
chalk.gray(measurement.entry),
];
let remoteMean;
let remoteSem;
if (remoteMasterResults) {
remoteMean = remoteMasterResults.benchmarks[benchmark].averages[i].mean;
remoteSem = remoteMasterResults.benchmarks[benchmark].averages[i].sem;
// https://en.wikipedia.org/wiki/1.96 gives a 99% confidence interval.
const ci95 = remoteSem * 1.96;
row.push(chalk.white(+remoteMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2)));
}
let localMean;
let localSem;
if (localResults) {
localMean = localResults.benchmarks[benchmark].averages[i].mean;
localSem = localResults.benchmarks[benchmark].averages[i].sem;
const ci95 = localSem * 1.96;
row.push(chalk.white(+localMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2)));
}
if (localResults && remoteMasterResults) {
row.push(percentChange(remoteMean, localMean, remoteSem, localSem));
}
table.push(row);
});
});
}
function addBundleSizeComparions(table, localResults, remoteMasterResults) {
const bundlesRowHeader = [chalk.white.bold('Bundles')];
if (remoteMasterResults) {
bundlesRowHeader.push(chalk.white.bold('Size'));
}
if (localResults) {
bundlesRowHeader.push(chalk.white.bold('Size'));
}
if (localResults && remoteMasterResults) {
bundlesRowHeader.push(chalk.white.bold('Diff'));
}
table.push(bundlesRowHeader);
const bundles = Object.keys(
(localResults && localResults.bundles.bundleSizes)
||
(remoteMasterResults && remoteMasterResults.bundles.bundleSizes)
);
bundles.forEach(bundle => {
const row = [
chalk.gray(bundle),
];
let remoteSize = 0;
if (remoteMasterResults) {
const remoteBundle = remoteSize = remoteMasterResults.bundles.bundleSizes[bundle];
if (remoteBundle) {
remoteSize = remoteSize.size;
}
row.push(chalk.white(remoteSize + ' kb'));
}
let localSize = 0;
if (localResults) {
const localBundle = localResults.bundles.bundleSizes[bundle];
if (localBundle) {
localSize = localBundle.size;
}
localSize = localResults.bundles.bundleSizes[bundle].size;
row.push(chalk.white(localSize + ' kb'));
}
if (localResults && remoteMasterResults) {
row.push(percentChange(remoteSize, localSize, 0, 0));
}
table.push(row);
});
}
function printResults(localResults, remoteMasterResults) {
const head = [''];
if (remoteMasterResults) {
head.push(chalk.yellow.bold('Remote (Merge Base)'));
}
if (localResults) {
head.push(chalk.green.bold('Local (Current Branch)'));
}
if (localResults && remoteMasterResults) {
head.push('');
}
const table = new Table({ head });
addBundleSizeComparions(table, localResults, remoteMasterResults);
addBenchmarkResults(table, localResults, remoteMasterResults);
console.log(table.toString());
}
module.exports = printResults;