refactor(benchmarks): align tree benchmark with largetable benchmark

- add ng2_switch benchmark to track `ngFor` over `ngSwitch`
- measure create only, createDestroy and update
- simplify the created dom
- always add a style binding
This commit is contained in:
Tobias Bosch 2016-09-01 16:56:45 -07:00 committed by Martin Probst
parent b4363bc8af
commit df4c0a3d1f
24 changed files with 381 additions and 324 deletions

View File

@ -8,62 +8,91 @@
import {runBenchmark, verifyNoBrowserErrors} from 'e2e_util/perf_util';
interface Worker {
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', () => {
afterEach(verifyNoBrowserErrors);
it('should run for ng2', (done) => {
runTreeBenchmark({
id: 'deepTree.ng2',
url: 'all/benchmarks/src/tree/ng2/index.html',
}).then(done, done.fail);
});
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => {
describe(worker.id, () => {
it('should run for ng2 static', (done) => {
runTreeBenchmark({
id: 'deepTree.ng2.static',
url: 'all/benchmarks/src/tree/ng2_static/index.html',
}).then(done, done.fail);
});
it('should run for ng2', (done) => {
runTreeBenchmark({
id: `deepTree.ng2.${worker.id}`,
url: 'all/benchmarks/src/tree/ng2/index.html',
}).then(done, done.fail);
});
it('should run for the baseline', (done) => {
runTreeBenchmark({
id: 'deepTree.baseline',
url: 'all/benchmarks/src/tree/baseline/index.html',
ignoreBrowserSynchronization: true,
}).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',
}).then(done, done.fail);
});
it('should run for the baseline dom', (done) => {
runTreeBenchmark({
id: 'deepTree.baselineDom',
url: 'all/benchmarks/src/tree/baseline_dom/index.html',
ignoreBrowserSynchronization: true,
}).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',
}).then(done, done.fail);
});
it('should run for the incremental dom', (done) => {
runTreeBenchmark({
id: 'deepTree.incrementalDom',
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
ignoreBrowserSynchronization: true,
}).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,
}).then(done, done.fail);
});
it('should run for polymer binary tree', (done) => {
runTreeBenchmark({
id: 'deepTree.polymer',
url: 'all/benchmarks/src/tree/polymer/index.html',
ignoreBrowserSynchronization: true,
}).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,
}).then(done, done.fail);
});
it('should run for polymer leaves', (done) => {
runTreeBenchmark({
id: 'deepTree.polymerLeaves',
url: 'all/benchmarks/src/tree/polymer_leaves/index.html',
ignoreBrowserSynchronization: true,
}).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,
}).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,
}).then(done, done.fail);
});
});
});
function runTreeBenchmark(

View File

@ -24,6 +24,12 @@ describe('tree benchmark spec', () => {
});
});
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',
@ -31,14 +37,7 @@ describe('tree benchmark spec', () => {
});
});
it('should work for the baseline dom', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/baseline_dom/index.html',
ignoreBrowserSynchronization: true,
});
});
it('should work for the incremental dom', () => {
it('should work for incremental dom', () => {
testTreeBenchmark({
url: 'all/benchmarks/src/tree/incremental_dom/index.html',
ignoreBrowserSynchronization: true,
@ -60,9 +59,15 @@ describe('tree benchmark spec', () => {
});
function testTreeBenchmark(openConfig: {url: string, ignoreBrowserSynchronization?: boolean}) {
openBrowser(openConfig);
openBrowser({
url: openConfig.url,
ignoreBrowserSynchronization: openConfig.ignoreBrowserSynchronization,
params: [{name: 'depth', value: 4}],
});
$('#createDom').click();
expect($('#root').getText()).toContain('0');
$('#createDom').click();
expect($('#root').getText()).toContain('A');
$('#destroyDom').click();
expect($('#root').getText()).toEqual('');
}

View File

@ -2,26 +2,26 @@
<html>
<body>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Baseline tree benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<h2>Baseline Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<div>
<tree id="root"></tree>
</div>
<div>
<tree id="root"></tree>
</div>
<script src="../../bootstrap_ng2.js"></script>
<script src="../../bootstrap_plain.js"></script>
</body>
</html>

View File

@ -1,19 +1,18 @@
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {BaseLineTreeComponent} from './tree';
import {TreeComponent} from './tree';
export function main() {
var app: BaseLineTreeComponent;
var tree: TreeComponent;
function destroyDom() { app.update(emptyTree); }
function destroyDom() { tree.data = emptyTree; }
function createDom() { app.update(buildTree()); }
function createDom() { tree.data = buildTree(); }
function noop() {}
function init() {
const tree: any = document.querySelector('tree');
app = new BaseLineTreeComponent(tree);
tree = new TreeComponent(document.querySelector('tree'));
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);

View File

@ -1,74 +1,61 @@
import {__platform_browser_private__} from '@angular/platform-browser';
import {TreeNode} from '../util';
// 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;
export class TreeComponent {
private _renderNodes: any[];
const BASELINE_TREE_TEMPLATE = document.createElement('template');
BASELINE_TREE_TEMPLATE.innerHTML =
'<span>_<template class="ng-provider"></template><template class="ng-provider"></template></span>';
const BASELINE_IF_TEMPLATE = document.createElement('template');
BASELINE_IF_TEMPLATE.innerHTML = '<span template="if"><tree></tree></span>';
constructor(private _rootEl: any) {}
export class BaseLineTreeComponent {
value: BaseLineInterpolation;
left: BaseLineIf;
right: BaseLineIf;
constructor(public element: HTMLElement) {
var clone = getDOM().clone(BASELINE_TREE_TEMPLATE.content.firstChild);
getDOM().appendChild(element, clone);
var child = clone.firstChild;
this.value = new BaseLineInterpolation(child);
child = getDOM().nextSibling(child);
this.left = new BaseLineIf(child);
child = getDOM().nextSibling(child);
this.right = new BaseLineIf(child);
}
update(value: TreeNode) {
this.value.update(value.value);
this.left.update(value.left);
this.right.update(value.right);
}
}
export class BaseLineInterpolation {
value: string;
constructor(public textNode: Node) { this.value = null; }
update(value: string) {
if (this.value !== value) {
this.value = value;
getDOM().setText(this.textNode, value + ' ');
set data(data: TreeNode) {
if (!data.left) {
this._destroy();
} else if (this._renderNodes) {
this._update(data, 0);
} else {
this._create(this._rootEl, data, 0);
}
}
}
export class BaseLineIf {
condition: boolean;
component: BaseLineTreeComponent;
constructor(public anchor: Node) {
this.condition = false;
this.component = null;
private _create(parentNode: any, dataNode: TreeNode, index: number) {
if (!this._renderNodes) {
this._renderNodes = new Array(dataNode.transitiveChildCount);
}
const span = document.createElement('span');
if (dataNode.depth % 2 === 0) {
span.style.backgroundColor = 'grey';
}
parentNode.appendChild(span);
this._renderNodes[index] = span;
this._updateNode(span, dataNode);
if (dataNode.left) {
const leftTree = document.createElement('tree');
parentNode.appendChild(leftTree);
this._create(leftTree, dataNode.left, index + 1);
}
if (dataNode.right) {
const rightTree = document.createElement('tree');
parentNode.appendChild(rightTree);
this._create(rightTree, dataNode.right, index + dataNode.left.transitiveChildCount + 1);
}
}
update(value: TreeNode) {
var newCondition = !!value;
if (this.condition !== newCondition) {
this.condition = newCondition;
if (this.component) {
getDOM().remove(this.component.element);
this.component = null;
}
if (this.condition) {
var element = getDOM().firstChild((<any>getDOM().clone(BASELINE_IF_TEMPLATE)).content);
this.anchor.parentNode.insertBefore(element, getDOM().nextSibling(this.anchor));
this.component = new BaseLineTreeComponent(<HTMLElement>getDOM().firstChild(element));
}
private _updateNode(renderNode: any, dataNode: TreeNode) {
renderNode.textContent = ` ${dataNode.value} `;
}
private _update(dataNode: TreeNode, index: number) {
this._updateNode(this._renderNodes[index], dataNode);
if (dataNode.left) {
this._update(dataNode.left, index + 1);
}
if (this.component) {
this.component.update(value);
if (dataNode.right) {
this._update(dataNode.right, index + dataNode.left.transitiveChildCount + 1);
}
}
private _destroy() {
while (this._rootEl.lastChild) this._rootEl.lastChild.remove();
this._renderNodes = null;
}
}

View File

@ -1,25 +0,0 @@
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {createTreeTemplate, destroyTreeTemplate} from './tree';
export function main() {
var app: any;
function destroyDom() { destroyTreeTemplate(app); }
function createDom() { createTreeTemplate(app, buildTree()); }
function noop() {}
function init() {
app = document.querySelector('tree');
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
}
init();
}

View File

@ -1,30 +0,0 @@
import {TreeNode} from '../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>
export function createTreeTemplate(parentEl: any, data: TreeNode) {
const rootSpan = document.createElement('span');
parentEl.appendChild(rootSpan);
rootSpan.appendChild(document.createTextNode(` ${data.value} `));
if (data.left) {
const leftTreeSpan = document.createElement('span');
rootSpan.appendChild(leftTreeSpan);
const leftTree = document.createElement('tree');
leftTreeSpan.appendChild(leftTree);
createTreeTemplate(leftTree, data.left);
}
if (data.right) {
const rightTreeSpan = document.createElement('span');
rootSpan.appendChild(rightTreeSpan);
const rightTree = document.createElement('tree');
rightTreeSpan.appendChild(rightTree);
createTreeTemplate(rightTree, data.right);
}
}
export function destroyTreeTemplate(el: any) {
while (el.firstChild) el.removeChild(el.firstChild);
}

View File

@ -10,7 +10,7 @@
<button>Apply</button>
</form>
<h2>Baseline tree benchmark</h2>
<h2>Incremental-Dom Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>

View File

@ -1,19 +1,19 @@
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {render} from './tree';
import {TreeComponent} from './tree';
const {patch} = require('incremental-dom');
export function main() {
var app: any;
var tree: TreeComponent;
function destroyDom() { patch(app, () => render(emptyTree)); }
function destroyDom() { tree.data = emptyTree; }
function createDom() { patch(app, () => render(buildTree())); }
function createDom() { tree.data = buildTree(); }
function noop() {}
function init() {
app = document.querySelector('tree');
tree = new TreeComponent(document.querySelector('tree'));
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);

View File

@ -1,26 +1,29 @@
import {TreeNode} from '../util';
const {elementOpen, elementClose, text} = require('incremental-dom');
const {patch, elementOpen, elementClose, elementOpenStart, elementOpenEnd, text, attr} =
require('incremental-dom');
// 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>
export function render(data: TreeNode) {
elementOpen('span', '', null);
text(` ${data.value} `);
if (data.left) {
elementOpen('span', '', null);
elementOpen('tree', '', null);
render(data.left);
elementClose('tree');
export class TreeComponent {
constructor(private _rootEl: any) {}
set data(data: TreeNode) { patch(this._rootEl, () => this._render(data)); }
private _render(data: TreeNode) {
elementOpenStart('span', '', null);
if (data.depth % 2 === 0) {
attr('style', 'background-color: grey');
}
elementOpenEnd();
text(` ${data.value} `);
elementClose('span');
if (data.left) {
elementOpen('tree', '', null);
this._render(data.left);
elementClose('tree');
}
if (data.right) {
elementOpen('tree', '', null);
this._render(data.right);
elementClose('tree');
}
}
if (data.right) {
elementOpen('span', '', null);
elementOpen('tree', '', null);
render(data.right);
elementClose('tree');
elementClose('span');
}
elementClose('span');
}

View File

@ -2,26 +2,26 @@
<html>
<body>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Angular2 tree benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<h2>Ng2 Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<div>
<tree id="root"></tree>
</div>
<div>
<tree id="root"></tree>
</div>
<script src="../../bootstrap_ng2.js"></script>
<script src="../../bootstrap_ng2.js"></script>
</body>
</html>
</html>

View File

@ -7,7 +7,7 @@ import {TreeNode, emptyTree} from '../util';
selector: 'tree',
inputs: ['data'],
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>`
`<span [style.backgroundColor]="data.depth % 2 ? '' : 'grey'"> {{data.value}} </span><tree *ngIf='data.right != null' [data]='data.right'></tree><tree *ngIf='data.left != null' [data]='data.left'></tree>`
})
export class TreeComponent {
data: TreeNode = emptyTree;

View File

@ -9,7 +9,7 @@
<button>Apply</button>
</form>
<h2>Angular2 tree benchmark</h2>
<h2>Ng2 Static Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>

View File

@ -5,9 +5,12 @@ import {TreeNode, emptyTree, maxDepth} from '../util';
function createTreeComponent(level: number, isLeaf: boolean) {
const nextTreeEl = `tree${level+1}`;
const template = isLeaf ?
`<span> {{data.value}} </span>` :
`<span> {{data.value}} <span><${nextTreeEl} [data]='data.right'></${nextTreeEl}></span><span><${nextTreeEl} [data]='data.left'></${nextTreeEl}></span></span>`;
let template =
`<span [style.backgroundColor]="data.depth % 2 ? '' : 'grey'"> {{data.value}} </span>`;
if (!isLeaf) {
template +=
`<${nextTreeEl} [data]='data.right'></${nextTreeEl}><${nextTreeEl} [data]='data.left'></${nextTreeEl}>`;
}
@Component({selector: `tree${level}`, template: template})
class TreeComponent {

View File

@ -10,7 +10,7 @@
<button>Apply</button>
</form>
<h2>Baseline tree benchmark</h2>
<h2>Ng2 with NgSwitch Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
@ -24,4 +24,4 @@
<script src="../../bootstrap_ng2.js"></script>
</body>
</html>
</html>

View File

@ -0,0 +1,40 @@
import {ApplicationRef, NgModule, enableProdMode} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {bindAction, profile} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {AppModule, TreeComponent} from './tree';
export function main() {
var tree: TreeComponent;
var appRef: ApplicationRef;
function destroyDom() {
tree.data = emptyTree;
appRef.tick();
}
function createDom() {
tree.data = buildTree();
appRef.tick();
}
function noop() {}
function init() {
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule).then((ref) => {
const injector = ref.injector;
appRef = injector.get(ApplicationRef);
tree = appRef.components[0].instance;
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
});
}
init();
}

View File

@ -0,0 +1,20 @@
import {Component, Input, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {TreeNode, emptyTree, flattenTree} from '../util';
@Component({
selector: 'tree',
template: `<ng-container [ngSwitch]="data.depth % 2">
<span *ngSwitchCase="0" style="background-color: grey"> {{data.value}} </span>
<span *ngSwitchDefault> {{data.value}} </span>
<tree *ngIf='data.right != null' [data]='data.right'></tree><tree *ngIf='data.left != null' [data]='data.left'></tree>`
})
export class TreeComponent {
@Input()
data: TreeNode = emptyTree;
}
@NgModule({imports: [BrowserModule], bootstrap: [TreeComponent], declarations: [TreeComponent]})
export class AppModule {
}

View File

@ -1,15 +1,13 @@
<link rel="import" href="/all/benchmarks/vendor/polymer/polymer.html">
<dom-module id="binary-tree">
<template>
<span>
<span>{{data.value}}</span>
<template is="dom-if" if="[[data.left]]">
<binary-tree data="[[data.left]]"></binary-tree>
</template>
<template is="dom-if" if="[[data.right]]">
<binary-tree data="[[data.right]]"></binary-tree>
</template>
</span>
<span style="[[data.style]]"> {{data.value}} </span>
<template is="dom-if" if="[[data.left]]">
<binary-tree data="[[data.left]]"></binary-tree>
</template>
<template is="dom-if" if="[[data.right]]">
<binary-tree data="[[data.right]]"></binary-tree>
</template>
</template>
<script>
Polymer({

View File

@ -1,29 +1,34 @@
<!doctype html>
<html>
<head>
<link rel="import" href="binary_tree.html">
</head>
<body>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Polymer tree benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
</p>
<h2>Polymer Tree Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<div>
<binary-tree id="root"></binary-tree>
</div>
<div>
<binary-tree id="root"></binary-tree>
</div>
<script src="../../bootstrap_plain.js"></script>
<script src="../../bootstrap_plain.js"></script>
</body>
</html>
</html>

View File

@ -1,4 +1,4 @@
import {bindAction} from '../../util';
import {bindAction, profile} from '../../util';
import {buildTree, emptyTree} from '../util';
declare var Polymer: any;
@ -11,6 +11,11 @@ export function main() {
function createDom() { rootEl.data = buildTree(); }
function noop() {}
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
bindAction('#updateDomProfile', profile(createDom, noop, 'update'));
bindAction('#createDomProfile', profile(createDom, destroyDom, 'create'));
}

View File

@ -1,29 +1,34 @@
<!doctype html>
<html>
<head>
<link rel="import" href="tree_leaf.html">
</head>
<body>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Params</h2>
<form>
Depth:
<input type="number" name="depth" placeholder="depth" value="9">
<br>
<button>Apply</button>
</form>
<h2>Polymer leaves benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
</p>
<h2>Polymer Leaves Benchmark</h2>
<p>
<button id="destroyDom">destroyDom</button>
<button id="createDom">createDom</button>
<button id="updateDomProfile">profile updateDom</button>
<button id="createDomProfile">profile createDom</button>
</p>
<div>
<binary-tree id="root"></binary-tree>
</div>
<div>
<binary-tree id="root"></binary-tree>
</div>
<script src="../../bootstrap_plain.js"></script>
<script src="../../bootstrap_plain.js"></script>
</body>
</html>

View File

@ -1,13 +1,11 @@
import {bindAction} from '../../util';
import {TreeNode, buildTree, emptyTree} from '../util';
import {TreeNode, buildTree, emptyTree, flattenTree} from '../util';
declare var Polymer: any;
export function main() {
const rootEl: any = document.querySelector('binary-tree');
rootEl.data = emptyTree;
function destroyDom() {
while (rootEl.firstChild) rootEl.removeChild(rootEl.firstChild);
}
@ -16,7 +14,7 @@ export function main() {
const flatTree = flattenTree(buildTree(), []);
for (var i = 0; i < flatTree.length; i++) {
const el: any = document.createElement('tree-leaf');
el.value = flatTree[i];
el.data = flatTree[i];
rootEl.appendChild(el);
}
}
@ -24,14 +22,3 @@ export function main() {
bindAction('#destroyDom', destroyDom);
bindAction('#createDom', createDom);
}
function flattenTree(node: TreeNode, target: string[]): string[] {
target.push(node.value);
if (node.left) {
flattenTree(node.left, target);
}
if (node.right) {
flattenTree(node.right, target);
}
return target;
}

View File

@ -1,13 +1,13 @@
<link rel="import" href="/all/benchmarks/vendor/polymer/polymer.html">
<dom-module id="tree-leaf">
<template>
<span>{{value}}</span>
<span style="[[data.style]]"> {{data.value}} </span>
</template>
<script>
Polymer({
is: 'tree-leaf',
properties: {
value: ''
data: {}
}
});
</script>

View File

@ -1,36 +1,62 @@
import {getIntParameter} from '../util';
export class TreeNode {
constructor(public value: string, public left: TreeNode, public right: TreeNode) {}
transitiveChildCount: number;
children: TreeNode[];
constructor(
public value: string, public depth: number, public maxDepth: number, public left: TreeNode,
public right: TreeNode) {
this.transitiveChildCount = Math.pow(2, (this.maxDepth - this.depth + 1)) - 1;
this.children = this.left ? [this.left, this.right] : [];
}
// Needed for Polymer as it does not support ternary nor modulo operator
// in expressions
get style(): string { return this.depth % 2 === 0 ? 'background-color: grey' : ''; }
}
let treeCreateCount: number;
export let maxDepth: number;
let numberData: string[];
let charData: string[];
let numberData: TreeNode;
let charData: TreeNode;
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));
}
numberData = _buildTree(0, numberValues);
charData = _buildTree(0, charValues);
}
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));
function _buildTree(currDepth: number, valueFn: (depth: number) => string): TreeNode {
const children = currDepth < maxDepth ? _buildTree(currDepth + 1, valueFn) : null;
return new TreeNode(valueFn(currDepth), currDepth, maxDepth, children, children);
}
export const emptyTree = new TreeNode('', null, null);
export const emptyTree = new TreeNode('', 0, 0, null, null);
export function buildTree(): TreeNode {
treeCreateCount++;
return _buildTree(treeCreateCount % 2 ? numberData : charData);
return treeCreateCount % 2 ? numberData : charData;
}
function numberValues(depth: number): string {
return depth.toString();
}
function charValues(depth: number): string {
return String.fromCharCode('A'.charCodeAt(0) + (depth % 26));
}
export function flattenTree(node: TreeNode, target: TreeNode[] = []): TreeNode[] {
target.push(node);
if (node.left) {
flattenTree(node.left, target);
}
if (node.right) {
flattenTree(node.right, target);
}
return target;
}