forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbenchmark.js
135 lines (117 loc) · 3.21 KB
/
benchmark.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
'use strict';
const Lighthouse = require('lighthouse');
const ChromeLauncher = require('lighthouse/lighthouse-cli/chrome-launcher.js')
.ChromeLauncher;
const stats = require('stats-analysis');
const config = require('lighthouse/lighthouse-core/config/perf.json');
const spawn = require('child_process').spawn;
const os = require('os');
const timesToRun = 10;
function wait(val) {
return new Promise(resolve => setTimeout(resolve, val));
}
async function runScenario(benchmark, launcher) {
const results = await Lighthouse(
`http://localhost:8080/${benchmark}/`,
{
output: 'json',
disableCpuThrottling: false,
disableNetworkThrottling: false,
},
config
);
const perfMarkings = results.audits['user-timings'].extendedInfo.value;
const entries = perfMarkings
.filter(marker => !marker.isMark)
.map(({duration, name}) => ({
entry: name,
time: duration,
}));
entries.push({
entry: 'First Meaningful Paint',
time: results.audits['first-meaningful-paint'].rawValue,
});
return entries;
}
function bootstrap(data) {
const len = data.length;
const arr = Array(len);
for (let j = 0; j < len; j++) {
arr[j] = data[(Math.random() * len) | 0];
}
return arr;
}
function calculateStandardErrorOfMean(data) {
const means = [];
for (let i = 0; i < 10000; i++) {
means.push(stats.mean(bootstrap(data)));
}
return stats.stdev(means);
}
function calculateAverages(runs) {
const data = [];
const averages = [];
runs.forEach((entries, x) => {
entries.forEach(({entry, time}, i) => {
if (i >= averages.length) {
data.push([time]);
averages.push({
entry,
mean: 0,
sem: 0,
});
} else {
data[i].push(time);
if (x === runs.length - 1) {
const dataWithoutOutliers = stats.filterMADoutliers(data[i]);
averages[i].mean = stats.mean(dataWithoutOutliers);
averages[i].sem = calculateStandardErrorOfMean(data[i]);
}
}
});
});
return averages;
}
async function initChrome() {
const platform = os.platform();
if (platform === 'linux') {
process.env.XVFBARGS = '-screen 0, 1024x768x16';
process.env.LIGHTHOUSE_CHROMIUM_PATH = 'chromium-browser';
const child = spawn('xvfb start', [{detached: true, stdio: ['ignore']}]);
child.unref();
// wait for chrome to load then continue
await wait(3000);
return child;
}
}
async function launchChrome(headless) {
let launcher;
try {
launcher = new ChromeLauncher({
additionalFlags: [headless ? '--headless' : ''],
});
await launcher.isDebuggerReady();
} catch (e) {
return launcher.run();
}
return launcher;
}
async function runBenchmark(benchmark, headless) {
const results = {
runs: [],
averages: [],
};
await initChrome();
for (let i = 0; i < timesToRun; i++) {
let launcher = await launchChrome(headless);
results.runs.push(await runScenario(benchmark, launcher));
// add a delay or sometimes it confuses lighthouse and it hangs
await wait(500);
try {
await launcher.kill();
} catch (e) {}
}
results.averages = calculateAverages(results.runs);
return results;
}
module.exports = runBenchmark;