refactor(benchmarks): make setup nicer

- simplify and correct systemjs config
- remove deep imports into Ng2 packages to work with bundles
- have separate Ng2 and Polymer bootstrap files
This commit is contained in:
Tobias Bosch
2016-08-30 14:17:37 -07:00
parent f7b5478e9f
commit 6ea5b05e7c
25 changed files with 260 additions and 320 deletions

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
(function(global: any /** TODO #9100 */) {
(function(global: any) {
writeScriptTag('/all/benchmarks/vendor/core.js');
writeScriptTag('/all/benchmarks/vendor/zone.js');
@ -18,13 +17,12 @@
(<any>global).benchmarksBootstrap = benchmarksBootstrap;
function benchmarksBootstrap() {
urlParamsToForm();
// check query param
var useBundles = location.search.indexOf('bundles=false') == -1;
if (useBundles) {
System.config({
defaultJSExtensions: true,
map: {
'index': 'index.js',
'@angular/core': '/packages-dist/core/bundles/core.umd.js',
'@angular/common': '/packages-dist/common/bundles/common.umd.js',
'@angular/forms': '/packages-dist/forms/bundles/forms.umd.js',
@ -40,8 +38,6 @@
'rxjs': '/all/benchmarks/vendor/rxjs'
},
packages: {
'app': {defaultExtension: 'js'},
'../app': {defaultExtension: 'js'},
'@angular/core/src/facade': {defaultExtension: 'js'},
'rxjs': {defaultExtension: 'js'}
}
@ -51,14 +47,9 @@
'Not using the Angular bundles. Don\'t use this configuration for e2e/performance tests!');
System.config({
map: {
'index': 'index.js',
'@angular': '/all/@angular',
'rxjs': '/all/benchmarks/vendor/rxjs'
},
defaultJSExtensions: true,
map: {'@angular': '/all/@angular', 'rxjs': '/all/benchmarks/vendor/rxjs'},
packages: {
'app': {defaultExtension: 'js'},
'../app': {defaultExtension: 'js'},
'@angular/core': {main: 'index.js', defaultExtension: 'js'},
'@angular/compiler': {main: 'index.js', defaultExtension: 'js'},
'@angular/router': {main: 'index.js', defaultExtension: 'js'},
@ -72,37 +63,11 @@
});
}
// BOOTSTRAP the app!
System.import('index').then(function(m: any /** TODO #9100 */) {
m.main();
}, console.error.bind(console));
System.import('index').then(function(m: any) { m.main(); }, console.error.bind(console));
}
function writeScriptTag(scriptUrl: any /** TODO #9100 */, onload?: any /** TODO #9100 */) {
function writeScriptTag(scriptUrl: string, onload?: string) {
document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`);
}
// helper script that will read out the url parameters
// and store them in appropriate form fields on the page
function urlParamsToForm() {
var regex = /(\w+)=(\w+)/g;
var search = decodeURIComponent(location.search);
var match: any[];
while (match = regex.exec(search)) {
var name = match[1];
var value = match[2];
var els = document.querySelectorAll('input[name="' + name + '"]');
var el: any;
for (var i = 0; i < els.length; i++) {
el = els[i];
if (el.type === 'radio' || el.type === 'checkbox') {
el.checked = el.value === value;
} else {
el.value = value;
}
}
}
}
}(window));

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function(global: any) {
writeScriptTag('/all/benchmarks/vendor/core.js');
writeScriptTag('/all/benchmarks/vendor/system.src.js', 'benchmarksBootstrap()');
(<any>global).benchmarksBootstrap = benchmarksBootstrap;
function benchmarksBootstrap() {
System.config({defaultJSExtensions: true});
// BOOTSTRAP the app!
System.import('index').then(function(m: any) { m.main(); }, console.error.bind(console));
}
function writeScriptTag(scriptUrl: string, onload?: string) {
document.write(`<script src="${scriptUrl}" onload="${onload}"></script>`);
}
}(window));

View File

@ -1,37 +0,0 @@
<!doctype html>
<html>
<body>
<ul>
<li>
<a href="di/di_benchmark.html">DI benchmark</a>
</li>
<li>
<a href="compiler/selector_benchmark.html">Selector benchmark</a>
</li>
<li>
<a href="compiler/compiler_benchmark.html">Compiler benchmark</a>
</li>
<li>
<a href="element_injector/element_injector_benchmark.html">Element injector benchmark</a>
</li>
<li>
<a href="tree/tree_benchmark.html">Tree benchmark</a>
</li>
<li>
<a href="static_tree/tree_benchmark.html">Static tree benchmark</a>
</li>
<li>
<a href="naive_infinite_scroll/index.html">Naive infinite scroll benchmark</a>
</li>
<li>
<a href="largetable/largetable_benchmark.html">Largetable benchmark</a>
</li>
<li>
<a href="costs/index.html">Benchmarks measuring costs of things</a>
</li>
<li>
<a href="page_load/page_load.html">Benchmark measuring time to bootstrap</a>
</li>
</ul>
</body>
</html>

View File

@ -1,67 +0,0 @@
import {getIntParameter, windowProfile, windowProfileEnd} from '@angular/platform-browser/testing/benchmark_util';
export class TreeNode {
constructor(public value: string, public left: TreeNode, public right: TreeNode) {}
}
let treeCreateCount: number;
let maxDepth: number;
let numberData: string[];
let charData: string[];
init();
function init() {
maxDepth = getIntParameter('depth');
treeCreateCount = 0;
numberData = [];
charData = [];
for (let i = 0; i < maxDepth; i++) {
numberData.push(i.toString());
charData.push(String.fromCharCode('A'.charCodeAt(0) + i));
}
}
function _buildTree(values: string[], curDepth: number = 0): TreeNode {
if (maxDepth === curDepth) return new TreeNode('', null, null);
return new TreeNode(
values[curDepth], _buildTree(values, curDepth + 1), _buildTree(values, curDepth + 1));
}
export function emptyTree(): TreeNode {
return new TreeNode('', null, null);
}
export function buildTree(): TreeNode {
treeCreateCount++;
return _buildTree(treeCreateCount % 2 ? numberData : charData);
}
export function profile(create: () => void, destroy: () => void, name: string) {
return function() {
windowProfile(name + ' w GC');
var duration = 0;
var count = 0;
while (count++ < 150) {
(<any>window)['gc']();
var start = window.performance.now();
create();
duration += window.performance.now() - start;
destroy();
}
windowProfileEnd(name + ' w GC');
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
windowProfile(name + ' w/o GC');
duration = 0;
count = 0;
while (count++ < 150) {
var start = window.performance.now();
create();
duration += window.performance.now() - start;
destroy();
}
windowProfileEnd(name + ' w/o GC');
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
};
}

View File

@ -19,9 +19,9 @@
</p>
<div>
<baseline></baseline>
<tree id="root"></tree>
</div>
<script src="../../bootstrap.js"></script>
<script src="../../bootstrap_ng2.js"></script>
</body>
</html>

View File

@ -1,23 +1,18 @@
import {BrowserDomAdapter} from '@angular/platform-browser/src/browser/browser_adapter';
import {bindAction} from '@angular/platform-browser/testing/benchmark_util';
import {TreeNode, buildTree, emptyTree, profile} from '../app/util';
import {BaseLineTreeComponent} from './app/tree';
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {BaseLineTreeComponent} from './tree';
export function main() {
var app: BaseLineTreeComponent;
function destroyDom() { app.update(emptyTree()); }
function destroyDom() { app.update(emptyTree); }
function createDom() { app.update(buildTree()); }
function noop() {}
function init() {
BrowserDomAdapter.makeCurrent();
const tree = document.createElement('tree');
document.querySelector('baseline').appendChild(tree);
const tree: any = document.querySelector('tree');
app = new BaseLineTreeComponent(tree);
bindAction('#destroyDom', destroyDom);

View File

@ -1,7 +1,11 @@
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {TreeNode} from '../../app/util';
import {__platform_browser_private__} from '@angular/platform-browser';
import {TreeNode} from '../util';
// http://jsperf.com/nextsibling-vs-childnodes
// Note: We are using the DomAdapter also in the Baseline
// so that Ng2 can actually reach the baseline. Once Ng2 is able to generate
// code that does not use the DomAdapter any more, we should remove this.
__platform_browser_private__.initDomAdapter();
const getDOM = __platform_browser_private__.getDOM;
const BASELINE_TREE_TEMPLATE = document.createElement('template');
BASELINE_TREE_TEMPLATE.innerHTML =

View File

@ -19,9 +19,9 @@
</p>
<div>
<app></app>
<tree id="root"></tree>
</div>
<script src="../../bootstrap.js"></script>
<script src="../../bootstrap_ng2.js"></script>
</body>
</html>

View File

@ -1,23 +1,22 @@
import {NgModule, enableProdMode} from '@angular/core';
import {ApplicationRef} from '@angular/core/src/application_ref';
import {ApplicationRef, NgModule, enableProdMode} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {bindAction, windowProfile, windowProfileEnd} from '@angular/platform-browser/testing/benchmark_util';
import {TreeNode, buildTree, emptyTree, profile} from '../app/util';
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {AppComponent, AppModule} from './app/tree';
import {AppModule, TreeComponent} from './tree';
export function main() {
var app: AppComponent;
var tree: TreeComponent;
var appRef: ApplicationRef;
function destroyDom() {
app.initData = emptyTree();
tree.data = emptyTree;
appRef.tick();
}
function createDom() {
app.initData = buildTree();
tree.data = buildTree();
appRef.tick();
}
@ -29,7 +28,7 @@ export function main() {
var injector = ref.injector;
appRef = injector.get(ApplicationRef);
app = appRef.components[0].instance;
tree = appRef.components[0].instance;
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
bindAction('#updateDomProfile', profile(createDom, noop, 'ng2-update'));

View File

@ -1,7 +1,7 @@
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {TreeNode, emptyTree} from '../../app/util';
import {TreeNode, emptyTree} from '../util';
@Component({
selector: 'tree',
@ -9,20 +9,10 @@ import {TreeNode, emptyTree} from '../../app/util';
template:
`<span> {{data.value}} <span template='ngIf data.right != null'><tree [data]='data.right'></tree></span><span template='ngIf data.left != null'><tree [data]='data.left'></tree></span></span>`
})
class TreeComponent {
data: TreeNode;
export class TreeComponent {
data: TreeNode = emptyTree;
}
@Component({selector: 'app', template: `<tree [data]='initData'></tree>`})
export class AppComponent {
initData: TreeNode;
constructor() { this.initData = emptyTree(); }
}
@NgModule({
imports: [BrowserModule],
bootstrap: [AppComponent],
declarations: [TreeComponent, AppComponent]
})
@NgModule({imports: [BrowserModule], bootstrap: [TreeComponent], declarations: [TreeComponent]})
export class AppModule {
}

View File

@ -20,10 +20,10 @@
</p>
<div>
<binary-tree id="app"></binary-tree>
<binary-tree id="root"></binary-tree>
</div>
<script src="../../bootstrap.js"></script>
<script src="../../bootstrap_polymer.js"></script>
</body>
</html>

View File

@ -1,14 +1,13 @@
import {bindAction} from '@angular/platform-browser/testing/benchmark_util';
import {buildTree, emptyTree} from '../app/util';
import {bindAction} from '../../util';
import {buildTree, emptyTree} from '../util';
declare var Polymer: any;
export function main() {
const rootEl: any = document.querySelector('binary-tree');
rootEl.data = emptyTree();
rootEl.data = emptyTree;
function destroyDom() { rootEl.data = emptyTree(); }
function destroyDom() { rootEl.data = emptyTree; }
function createDom() { rootEl.data = buildTree(); }

View File

@ -13,17 +13,17 @@
<button>Apply</button>
</form>
<h2>Polymer tree benchmark</h2>
<h2>Polymer leaves benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
</p>
<div>
<binary-tree id="app"></binary-tree>
<binary-tree id="root"></binary-tree>
</div>
<script src="../../bootstrap.js"></script>
<script src="../../bootstrap_polymer.js"></script>
</body>
</html>

View File

@ -1,13 +1,12 @@
import {bindAction} from '@angular/platform-browser/testing/benchmark_util';
import {TreeNode, buildTree, emptyTree} from '../app/util';
import {bindAction} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
declare var Polymer: any;
export function main() {
const rootEl: any = document.querySelector('binary-tree');
rootEl.data = emptyTree();
rootEl.data = emptyTree;
function destroyDom() {
while (rootEl.firstChild) rootEl.removeChild(rootEl.firstChild);

View File

@ -0,0 +1,36 @@
import {getIntParameter} from '../util';
export class TreeNode {
constructor(public value: string, public left: TreeNode, public right: TreeNode) {}
}
let treeCreateCount: number;
let maxDepth: number;
let numberData: string[];
let charData: string[];
init();
function init() {
maxDepth = getIntParameter('depth');
treeCreateCount = 0;
numberData = [];
charData = [];
for (let i = 0; i < maxDepth; i++) {
numberData.push(i.toString());
charData.push(String.fromCharCode('A'.charCodeAt(0) + i));
}
}
function _buildTree(values: string[], curDepth: number = 0): TreeNode {
if (maxDepth === curDepth) return new TreeNode('', null, null);
return new TreeNode(
values[curDepth], _buildTree(values, curDepth + 1), _buildTree(values, curDepth + 1));
}
export const emptyTree = new TreeNode('', null, null);
export function buildTree(): TreeNode {
treeCreateCount++;
return _buildTree(treeCreateCount % 2 ? numberData : charData);
}

View File

@ -0,0 +1,90 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
urlParamsToForm();
export function getIntParameter(name: string) {
return parseInt(getStringParameter(name), 10);
}
export function getStringParameter(name: string) {
var els = document.querySelectorAll(`input[name="${name}"]`);
var value: any;
var el: any;
for (var i = 0; i < els.length; i++) {
el = els[i];
var type = el.type;
if ((type != 'radio' && type != 'checkbox') || el.checked) {
value = el.value;
break;
}
}
if (value == null) {
throw new Error(`Could not find and input field with name ${name}`);
}
return value;
}
export function bindAction(selector: string, callback: () => void) {
document.querySelector(selector).addEventListener('click', callback);
}
export function profile(create: () => void, destroy: () => void, name: string) {
return function() {
window.console.profile(name + ' w GC');
var duration = 0;
var count = 0;
while (count++ < 150) {
(<any>window)['gc']();
var start = window.performance.now();
create();
duration += window.performance.now() - start;
destroy();
}
window.console.profileEnd();
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
window.console.profile(name + ' w/o GC');
duration = 0;
count = 0;
while (count++ < 150) {
var start = window.performance.now();
create();
duration += window.performance.now() - start;
destroy();
}
window.console.profileEnd();
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
};
}
// helper script that will read out the url parameters
// and store them in appropriate form fields on the page
function urlParamsToForm() {
var regex = /(\w+)=(\w+)/g;
var search = decodeURIComponent(location.search);
var match: any[];
while (match = regex.exec(search)) {
var name = match[1];
var value = match[2];
var els = document.querySelectorAll('input[name="' + name + '"]');
var el: any;
for (var i = 0; i < els.length; i++) {
el = els[i];
if (el.type === 'radio' || el.type === 'checkbox') {
el.checked = el.value === value;
} else {
el.value = value;
}
}
}
}