Skip to content
This repository has been archived by the owner on Sep 7, 2022. It is now read-only.

Node perf tester #4

Merged
merged 4 commits into from
Oct 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
bower_components
node_modules
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,24 @@ An element to help performance test other elements.

The perf-tester element accepts an array of `tests` which are html files containing perf tests to run. A simple "runner.html" as is shown in `/demo` can be created to display output.

Each test file should load `perf.js` and call `console.perf()` to start the test and `console.perfEnd()` to finish it.
Each test file should load `perf.js` and call `console.perf()` to start the test and `console.perfEnd()` to finish it.

### Node script

Run the node-script with

node node-perf-tester.js

The available options are:

--runs Number of runs to measure on
--targets The file location of a JSON-structured file with the following format:

[
["<type>", "<url>"]
]

where type is the name of the type you are testing and the url the location of the target under test

--port The port number to connect the protocol to. Use this when you are connecting to a separate device. If this argument is not provided, chrome-headless is used.
--throttling one of [FAST_3G, SLOW_3G] If not running in Chrome headless, you can enable throttling with one of the two options.
98 changes: 98 additions & 0 deletions node-perf-tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
const path = require('path');

const CDP = require('chrome-remote-interface');

const argv = require('minimist')(process.argv.slice(2));

const NUMBER_OF_RUNS = argv.runs || 100;

(async function() {
let port = argv.port;
let chrome;

if (!argv.port) {
const chromeLauncher = require('chrome-launcher');
chrome = await chromeLauncher.launch({
chromeFlags:
['--headless', '--disable-gpu', '--remote-debugging-address=0.0.0.0'],
port: 0
});
port = chrome.port;
}

const tab = await CDP.New({port});
const client = await CDP({tab, port});

const {Page, Network, Runtime} = client;

const ONE_MB = 1024 * 1024 / 8;
const throttling = {
FAST_3G: {
downloadThroughput: 1.6 * ONE_MB * .9,
uploadThroughput: .75 * ONE_MB * .9
},
SLOW_3G: {
downloadThroughput: .5 * ONE_MB * .8,
uploadThroughput: .5 * ONE_MB * .8
}
};

await Promise.all([
Page.enable(),
Network.enable(),
port && argv.throttling &&
Network.emulateNetworkConditions(Object.assign(
{}, throttling[argv.throttling], {offline: false, latency: 10})),
Network.clearBrowserCache(),
Network.setCacheDisabled({cacheDisabled: true}),
Network.setBypassServiceWorker({bypass: true}),
]);

let loadEventPromise;

Page.loadEventFired(() => {
loadEventPromise();
});

const options = require(path.join(process.cwd(), argv.targets));

const perfTimings = {};
for (const [type] of options) {
perfTimings[type] = [];
}

process.on('exit', async () => {
for (const [type, timings] of Object.entries(perfTimings)) {
const average = timings.reduce((a, b) => a + b) / timings.length;
console.log(
`Average gain for ${type} in ${timings.length} runs is ${average}`);
}

await CDP.Close({port, id: tab.id});
await client.close();
if (!argv.port) {
await chrome.kill();
}
});

for (let i = 0; i < NUMBER_OF_RUNS; i++) {
for (const [type, url] of options) {
requestType = type;

Page.navigate({url});

await new Promise(resolve => {
loadEventPromise = resolve;
});
const {result: {value: perfTiming}} =
await Runtime.evaluate({expression: 'window.perfTiming'});
// const {result: {value: perfTiming}} = await
// Runtime.evaluate({expression: 'window.performance.timing.loadEventEnd-
// window.performance.timing.navigationStart'});
perfTimings[type].push(perfTiming);
}
process.stdout.write(`${i + 1}/${NUMBER_OF_RUNS}\r`);
}

process.exit(0);
})()
1 change: 1 addition & 0 deletions options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "perf-tester",
"version": "0.0.1",
"main": "node-perf-tester.js",
"license": "BSD-3-Clause",
"dependencies": {
"chrome-launcher": "^0.4.0",
"chrome-remote-interface": "^0.24.3",
"minimist": "^1.2.0"
}
}
135 changes: 69 additions & 66 deletions perf.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,69 @@
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http:polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http:polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http:polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http:polymer.github.io/PATENTS.txt
*/
// x-browser compat.
(function() {
let wcr = false;

addEventListener('WebComponentsReady', function() {
wcr = true;
});

console.perf = function() {
if (window.HTMLImports && !HTMLImports.useNative && !wcr) {
let fn = console._perf.bind(console);
HTMLImports.whenReady(fn);
} else {
console._perf();
}
};

console._perf = function() {
if (window.gc) {
for (let i=0; i<20; i++) {
gc();
}
}
if (console.time) {
console.time('perf');
}
console.perf.time = performance.now();
};

console.perfEnd = function(info) {
if (window.WebComponents) {
if (!wcr) {
addEventListener('WebComponentsReady', function() {
console._perfEnd(info);
});
} else {
console._perfEnd(info);
}
} else {
console._perfEnd(info);
}
};

console._perfEnd = function(info) {
// force layout
document.body.offsetWidth;
let time = performance.now() - console.perf.time;
if (console.time) {
console.timeEnd('perf');
}
document.title = time.toFixed(1) + 'ms: ' + document.title;
if (window.top !== window) {
window.top.postMessage({time: time + 'ms', info: info}, '*');
}
};

})();
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http:polymer.github.io/LICENSE.txt The complete set of authors may be found
* at http:polymer.github.io/AUTHORS.txt The complete set of contributors may be
* found at http:polymer.github.io/CONTRIBUTORS.txt Code distributed by Google
* as part of the polymer project is also subject to an additional IP rights
* grant found at http:polymer.github.io/PATENTS.txt
*/
// x-browser compat.
(function() {
let wcr = false;

addEventListener('WebComponentsReady', function() {
wcr = true;
});

console.perf = function() {
if (window.HTMLImports && !HTMLImports.useNative && !wcr) {
let fn = console._perf.bind(console);
HTMLImports.whenReady(fn);
} else {
console._perf();
}
};

console._perf = function() {
if (window.gc) {
for (let i = 0; i < 20; i++) {
gc();
}
}
if (console.time) {
console.time('perf');
}
console.perf.time = performance.now();
};

console.perfEnd = function(info) {
if (window.WebComponents) {
if (!wcr) {
addEventListener('WebComponentsReady', function() {
console._perfEnd(info);
});
} else {
console._perfEnd(info);
}
} else {
console._perfEnd(info);
}
};

console._perfEnd = function(info, options = {}) {
if (!options.skipForceLayout) {
// force layout
document.body.offsetWidth;
}
window.perfTiming = performance.now() - console.perf.time;
if (console.time) {
console.timeEnd('perf');
}
document.title = window.perfTiming.toFixed(1) + 'ms: ' + document.title;
if (window.top !== window) {
window.top.postMessage({time: window.perfTiming + 'ms', info}, '*');
}
};

})();
Loading