fix(core): benchmarks - enable ng1 benchmark again

Also make it match the ng2 benchmark.
This commit is contained in:
Tobias Bosch 2017-04-13 15:00:59 -07:00
parent 2f442062d2
commit bccfaa46ec
6 changed files with 344 additions and 229 deletions

View File

@ -0,0 +1,81 @@
/**
* @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
*/
import {$} from 'protractor';
export const CreateBtn = '#createDom';
export const DestroyBtn = '#destroyDom';
export const DetectChangesBtn = '#detectChanges';
export const RootEl = '#root';
export const NumberOfChecksEl = '#numberOfChecks';
export interface Benchmark {
id: string;
url: string;
buttons: string[];
ignoreBrowserSynchronization?: boolean;
extraParams?: {name: string, value: any}[];
}
const CreateDestroyButtons: string[] = [CreateBtn, DestroyBtn];
const CreateDestroyDetectChangesButtons: string[] = [...CreateDestroyButtons, DetectChangesBtn];
export const Benchmarks: Benchmark[] = [
{
id: `deepTree.ng2`,
url: 'all/benchmarks/src/tree/ng2/index.html',
buttons: CreateDestroyDetectChangesButtons,
},
{
id: `deepTree.ng2.next`,
url: 'all/benchmarks/src/tree/ng2_next/index.html',
buttons: CreateDestroyDetectChangesButtons,
ignoreBrowserSynchronization: true,
// Can't use bundles as we use non exported code
extraParams: [{name: 'bundles', value: false}]
},
{
id: `deepTree.ng2.static`,
url: 'all/benchmarks/src/tree/ng2_static/index.html',
buttons: CreateDestroyButtons,
},
{
id: `deepTree.ng2_switch`,
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
buttons: CreateDestroyButtons,
},
{
id: `deepTree.baseline`,
url: 'all/benchmarks/src/tree/baseline/index.html',
buttons: CreateDestroyButtons,
ignoreBrowserSynchronization: true,
},
{
id: `deepTree.incremental_dom`,
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
buttons: CreateDestroyButtons,
ignoreBrowserSynchronization: true,
},
{
id: `deepTree.polymer`,
url: 'all/benchmarks/src/tree/polymer/index.html',
buttons: CreateDestroyButtons,
ignoreBrowserSynchronization: true,
},
{
id: `deepTree.polymer_leaves`,
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
buttons: CreateDestroyButtons,
ignoreBrowserSynchronization: true,
},
{
id: `deepTree.ng1`,
url: 'all/benchmarks/src/tree/ng1/index.html',
buttons: CreateDestroyDetectChangesButtons,
}
];

View File

@ -7,162 +7,77 @@
*/ */
import {runBenchmark, verifyNoBrowserErrors} from 'e2e_util/perf_util'; import {runBenchmark, verifyNoBrowserErrors} from 'e2e_util/perf_util';
import {$} from 'protractor'; import {$, browser} from 'protractor';
interface Worker { import {Benchmark, Benchmarks, CreateBtn, DestroyBtn, DetectChangesBtn, RootEl} from './tree_data';
id: string;
prepare?(): void;
work(): void;
}
const CreateOnlyWorker: Worker = {
id: 'createOnly',
prepare: () => $('#destroyDom').click(),
work: () => $('#createDom').click()
};
const CreateAndDestroyWorker: Worker = {
id: 'createDestroy',
work: () => {
$('#createDom').click();
$('#destroyDom').click();
}
};
const UpdateWorker: Worker = {
id: 'update',
work: () => $('#createDom').click()
};
describe('tree benchmark perf', () => { describe('tree benchmark perf', () => {
afterEach(verifyNoBrowserErrors); let _oldRootEl: any;
beforeEach(() => _oldRootEl = browser.rootEl);
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => { afterEach(() => {
describe(worker.id, () => { browser.rootEl = _oldRootEl;
verifyNoBrowserErrors();
});
it('should run for ng2', (done) => { Benchmarks.forEach(benchmark => {
describe(benchmark.id, () => {
it('should work for createOnly', (done) => {
runTreeBenchmark({ runTreeBenchmark({
id: `deepTree.ng2.${worker.id}`, id: 'createOnly',
url: 'all/benchmarks/src/tree/ng2/index.html', benchmark,
work: worker.work, prepare: () => $(CreateBtn).click(),
prepare: worker.prepare, work: () => $(DestroyBtn).click()
}).then(done, done.fail); }).then(done, done.fail);
}); });
it('should run for ng2 next', (done) => { it('should work for createDestroy', (done) => {
runTreeBenchmark({ runTreeBenchmark({
id: `deepTree.ng2.next.${worker.id}`, id: 'createDestroy',
url: 'all/benchmarks/src/tree/ng2_next/index.html', benchmark,
ignoreBrowserSynchronization: true, work: () => {
work: worker.work, $(DestroyBtn).click();
prepare: worker.prepare, $(CreateBtn).click();
// Can't use bundles as we use non exported code
extraParams: [{name: 'bundles', value: false}]
}).then(done, done.fail);
});
it('should run for ng2 static', (done) => {
runTreeBenchmark({
id: `deepTree.ng2.static.${worker.id}`,
url: 'all/benchmarks/src/tree/ng2_static/index.html',
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
it('should run for ng2 switch', (done) => {
runTreeBenchmark({
id: `deepTree.ng2_switch.${worker.id}`,
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
it('should run for the baseline', (done) => {
runTreeBenchmark({
id: `deepTree.baseline.${worker.id}`,
url: 'all/benchmarks/src/tree/baseline/index.html',
ignoreBrowserSynchronization: true,
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
it('should run for incremental-dom', (done) => {
runTreeBenchmark({
id: `deepTree.incremental_dom.${worker.id}`,
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
ignoreBrowserSynchronization: true,
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
it('should run for polymer binary tree', (done) => {
runTreeBenchmark({
id: `deepTree.polymer.${worker.id}`,
url: 'all/benchmarks/src/tree/polymer/index.html',
ignoreBrowserSynchronization: true,
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
it('should run for polymer leaves', (done) => {
runTreeBenchmark({
id: `deepTree.polymer_leaves.${worker.id}`,
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
ignoreBrowserSynchronization: true,
work: worker.work,
prepare: worker.prepare,
}).then(done, done.fail);
});
});
});
it('should run ng2 changedetection', (done) => {
runTreeBenchmark({
id: `deepTree.ng2.changedetection`,
url: 'all/benchmarks/src/tree/ng2/index.html',
work: () => $('#detectChanges').click(),
setup: () => $('#createDom').click(),
}).then(done, done.fail);
});
it('should run ng2 next changedetection', (done) => {
runTreeBenchmark({
id: `deepTree.ng2.next.changedetection`,
url: 'all/benchmarks/src/tree/ng2_next/index.html',
work: () => $('#detectChanges').click(),
setup: () => $('#createDom').click(),
ignoreBrowserSynchronization: true,
// Can't use bundles as we use non exported code
extraParams: [{name: 'bundles', value: false}]
}).then(done, done.fail);
});
function runTreeBenchmark(config: {
id: string,
url: string, ignoreBrowserSynchronization?: boolean,
work: () => any,
prepare?: () => any,
extraParams?: {name: string, value: any}[],
setup?: () => any
}) {
let params = [{name: 'depth', value: 11}];
if (config.extraParams) {
params = params.concat(config.extraParams);
} }
return runBenchmark({ }).then(done, done.fail);
id: config.id, });
url: config.url,
ignoreBrowserSynchronization: config.ignoreBrowserSynchronization, it('should work for update', (done) => {
params: params, runTreeBenchmark({id: 'update', benchmark, work: () => $(CreateBtn).click()})
work: config.work, .then(done, done.fail);
prepare: config.prepare, });
setup: config.setup
if (benchmark.buttons.indexOf(DetectChangesBtn) !== -1) {
it('should work for detectChanges', (done) => {
runTreeBenchmark({
id: 'detectChanges',
benchmark,
work: () => $(DetectChangesBtn).click(),
setup: () => $(DestroyBtn).click()
}).then(done, done.fail);
}); });
} }
});
});
}); });
function runTreeBenchmark({id, benchmark, prepare, setup, work}: {
id: string; benchmark: Benchmark, prepare ? () : void; setup ? () : void; work(): void;
}) {
let params = [{name: 'depth', value: 11}];
if (benchmark.extraParams) {
params = params.concat(benchmark.extraParams);
}
browser.rootEl = RootEl;
return runBenchmark({
id: `${benchmark.id}.${id}`,
url: benchmark.url,
ignoreBrowserSynchronization: benchmark.ignoreBrowserSynchronization,
params: params,
work: work,
prepare: prepare,
setup: setup
});
}

View File

@ -7,107 +7,57 @@
*/ */
import {openBrowser, verifyNoBrowserErrors} from 'e2e_util/e2e_util'; import {openBrowser, verifyNoBrowserErrors} from 'e2e_util/e2e_util';
import {$} from 'protractor'; import {$, browser} from 'protractor';
import {Benchmark, Benchmarks, CreateBtn, DestroyBtn, DetectChangesBtn, NumberOfChecksEl, RootEl} from './tree_data';
describe('tree benchmark spec', () => { describe('tree benchmark spec', () => {
afterEach(verifyNoBrowserErrors); let _oldRootEl: any;
beforeEach(() => _oldRootEl = browser.rootEl);
it('should work for ng2', () => { afterEach(() => {
testTreeBenchmark({ browser.rootEl = _oldRootEl;
url: 'all/benchmarks/src/tree/ng2/index.html', verifyNoBrowserErrors();
});
}); });
it('should work for ng2 detect changes', () => { Benchmarks.forEach(benchmark => {
let params = [{name: 'depth', value: 4}]; describe(benchmark.id, () => {
openBrowser({url: 'all/benchmarks/src/tree/ng2/index.html', params}); it('should work for createDestroy', () => {
$('#detectChanges').click(); openTreeBenchmark(benchmark);
expect($('#numberOfChecks').getText()).toContain('10'); $(CreateBtn).click();
expect($(RootEl).getText()).toContain('0');
$(DestroyBtn).click();
expect($(RootEl).getText()).toEqual('');
}); });
it('should work for ng2 next', () => { it('should work for update', () => {
testTreeBenchmark({ openTreeBenchmark(benchmark);
url: 'all/benchmarks/src/tree/ng2_next/index.html', $(CreateBtn).click();
ignoreBrowserSynchronization: true, $(CreateBtn).click();
// Can't use bundles as we use non exported code expect($(RootEl).getText()).toContain('A');
extraParams: [{name: 'bundles', value: false}]
});
}); });
it('should work for ng2 next detect changes', () => { if (benchmark.buttons.indexOf(DetectChangesBtn) !== -1) {
let params = [ it('should work for detectChanges', () => {
{name: 'depth', value: 4}, openTreeBenchmark(benchmark);
// Can't use bundles as we use non exported code $(DetectChangesBtn).click();
{name: 'bundles', value: false} expect($(NumberOfChecksEl).getText()).toContain('10');
];
openBrowser({
url: 'all/benchmarks/src/tree/ng2_next/index.html',
ignoreBrowserSynchronization: true, params
}); });
$('#detectChanges').click();
expect($('#numberOfChecks').getText()).toContain('10');
});
it('should work for ng2 static', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/ng2_static/index.html',
});
});
it('should work for ng2 switch', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/ng2_switch/index.html',
});
});
it('should work for the baseline', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/baseline/index.html',
ignoreBrowserSynchronization: true,
});
});
it('should work for incremental dom', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
ignoreBrowserSynchronization: true,
});
});
it('should work for polymer binary tree', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/polymer/index.html',
ignoreBrowserSynchronization: true,
});
});
it('should work for polymer leaves', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
ignoreBrowserSynchronization: true,
});
});
function testTreeBenchmark(openConfig: {
url: string,
ignoreBrowserSynchronization?: boolean,
extraParams?: {name: string, value: any}[]
}) {
let params = [{name: 'depth', value: 4}];
if (openConfig.extraParams) {
params = params.concat(openConfig.extraParams);
} }
});
});
function openTreeBenchmark(benchmark: Benchmark) {
let params = [{name: 'depth', value: 4}];
if (benchmark.extraParams) {
params = params.concat(benchmark.extraParams);
}
browser.rootEl = RootEl;
openBrowser({ openBrowser({
url: openConfig.url, url: benchmark.url,
ignoreBrowserSynchronization: openConfig.ignoreBrowserSynchronization, ignoreBrowserSynchronization: benchmark.ignoreBrowserSynchronization,
params: params, params: params,
}); });
$('#createDom').click();
expect($('#root').getText()).toContain('0');
$('#createDom').click();
expect($('#root').getText()).toContain('A');
$('#destroyDom').click();
expect($('#root').getText()).toEqual('');
} }
}); });

View File

@ -0,0 +1,44 @@
<!doctype html>
<html>
<body>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Ng1 Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="detectChanges">detectChanges</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
<button id="detectChangesProfile">profile detectChanges</button>
</p>
<div>
Change detection runs:<span id="numberOfChecks"></span>
</div>
<div>
<tree id="root" data="initData">Loading...</tree>
</div>
<script>
var mainUrls = [
'/all/benchmarks/vendor/angular.js',
'../../bootstrap_plain.js'
];
var mainUrl = window.location.search.split(/[?&]main=([^&]+)/)[1];
if (mainUrl) {
mainUrls = [mainUrl];
}
mainUrls.forEach(function(mainUrl) {
document.write('<script src="' + mainUrl + '">\u003c/script>');
});
</script>
</body>
</html>

View File

@ -0,0 +1,53 @@
/**
* @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
*/
import {bindAction, profile} from '../../util';
import {buildTree, emptyTree} from '../util';
import {addTreeToModule} from './tree';
declare var angular: any;
function init() {
let detectChangesRuns = 0;
const numberOfChecksEl = document.getElementById('numberOfChecks') !;
addTreeToModule(angular.module('app', [])).run([
'$rootScope',
($rootScope: any) => {
function detectChanges() {
for (let i = 0; i < 10; i++) {
$rootScope.$digest();
}
detectChangesRuns += 10;
numberOfChecksEl.textContent = `${detectChangesRuns}`;
}
function noop() {}
function destroyDom() {
$rootScope.$apply(() => { $rootScope.initData = emptyTree; });
}
function createDom() {
$rootScope.$apply(() => { $rootScope.initData = buildTree(); });
}
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
bindAction('#detectChanges', detectChanges);
bindAction('#detectChangesProfile', profile(detectChanges, noop, 'detectChanges'));
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
}
]);
angular.bootstrap(document.querySelector('tree'), ['app']);
}
init();

View File

@ -0,0 +1,72 @@
/**
* @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
*/
import {TreeNode} from '../util';
declare var angular: any;
export function addTreeToModule(mod: any): any {
return mod
.directive(
'tree',
function() {
return {
scope: {data: '='},
template:
`<span ng-style="{'background-color': data.depth % 2 ? '' : 'grey'}"> {{data.value}} </span><tree-if data='data.right'></tree-if><tree-if data='data.left'></tree-if>`
};
})
// special directive for "if" as angular 1.3 does not support
// recursive components.
// Cloned from real ngIf directive, but using a lazily created transclude function.
.directive(
'treeIf',
[
'$compile', '$animate',
function($compile: any, $animate: any) {
let transcludeFn: any;
return {
transclude: 'element',
priority: 600,
terminal: true,
$$tlb: true,
link: function($scope: any, $element: any, $attr: any, ctrl: any) {
if (!transcludeFn) {
const template = '<tree data="' + $attr.data + '"></tree>';
transcludeFn = $compile(template);
}
let childElement: any, childScope: any;
$scope.$watch($attr.data, function ngIfWatchAction(value: any) {
if (value) {
if (!childScope) {
childScope = $scope.$new();
transcludeFn(childScope, function(clone: any) {
childElement = clone;
$animate.enter(clone, $element.parent(), $element);
});
}
} else {
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (childElement) {
$animate.leave(childElement);
childElement = null;
}
}
});
}
};
}
])
.config([
'$compileProvider',
function($compileProvider: any) { $compileProvider.debugInfoEnabled(false); }
]);
}