angular-cn/modules/benchmarks/src/tree/tree_benchmark.js
Tobias Bosch bfa381b35a refactor(view): introduce AppViewManager to consolidate logic
AppViewManager is the single entry point to changing the view hierarchy.
It is split between the manager itself which does coordination and
helper methods, so both are easily testable in isolation.

Also, ViewContainer is now only a pure reference to a bound element
with the previous functionality but does not contain the list of views
any more.

Part of #1477
2015-04-24 20:26:18 -07:00

251 lines
7.5 KiB
JavaScript

import {bootstrap, Component, Viewport, View, ViewContainer, Compiler, NgElement, Decorator} from 'angular2/angular2';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {reflector} from 'angular2/src/reflection/reflection';
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {isPresent} from 'angular2/src/facade/lang';
import {window, document, gc} from 'angular2/src/facade/browser';
import {getIntParameter, getStringParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
import {If} from 'angular2/directives';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import {APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
import * as rvf from 'angular2/src/render/dom/view/view_factory';
import {bind} from 'angular2/di';
function createBindings():List {
var viewCacheCapacity = getStringParameter('viewcache') == 'true' ? 10000 : 1;
return [
bind(rvf.VIEW_POOL_CAPACITY).toValue(viewCacheCapacity),
bind(APP_VIEW_POOL_CAPACITY).toValue(viewCacheCapacity)
];
}
function setupReflector() {
reflector.reflectionCapabilities = new ReflectionCapabilities();
}
var BASELINE_TREE_TEMPLATE;
var BASELINE_IF_TEMPLATE;
export function main() {
BrowserDomAdapter.makeCurrent();
var maxDepth = getIntParameter('depth');
setupReflector();
BASELINE_TREE_TEMPLATE = DOM.createTemplate(
'<span>_<template class="ng-binding"></template><template class="ng-binding"></template></span>');
BASELINE_IF_TEMPLATE = DOM.createTemplate(
'<span template="if"><tree></tree></span>');
var app;
var lifeCycle;
var baselineRootTreeComponent;
var count = 0;
function ng2DestroyDom() {
// TODO: We need an initial value as otherwise the getter for data.value will fail
// --> this should be already caught in change detection!
app.initData = new TreeNode('', null, null);
lifeCycle.tick();
}
function profile(create, destroy, name) {
return function() {
window.console.profile(name + ' w GC');
var duration = 0;
var count = 0;
while(count++ < 150) {
gc();
var start = window.performance.now();
create();
duration += window.performance.now() - start;
destroy();
}
window.console.profileEnd(name + ' w GC');
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(name + ' w/o GC');
window.console.log(`Iterations: ${count}; time: ${duration / count} ms / iteration`);
};
}
function ng2CreateDom() {
var values = count++ % 2 == 0 ?
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] :
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-'];
app.initData = buildTree(maxDepth, values, 0);
lifeCycle.tick();
}
function noop() {}
function initNg2() {
bootstrap(AppComponent, createBindings()).then((ref) => {
var injector = ref.injector;
lifeCycle = injector.get(LifeCycle);
app = injector.get(AppComponent);
bindAction('#ng2DestroyDom', ng2DestroyDom);
bindAction('#ng2CreateDom', ng2CreateDom);
bindAction('#ng2UpdateDomProfile', profile(ng2CreateDom, noop, 'ng2-update'));
bindAction('#ng2CreateDomProfile', profile(ng2CreateDom, ng2DestroyDom, 'ng2-create'));
});
}
function baselineDestroyDom() {
baselineRootTreeComponent.update(new TreeNode('', null, null));
}
function baselineCreateDom() {
var values = count++ % 2 == 0 ?
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] :
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-'];
baselineRootTreeComponent.update(buildTree(maxDepth, values, 0));
}
function initBaseline() {
var tree = DOM.createElement('tree');
DOM.appendChild(DOM.querySelector(document, 'baseline'), tree);
baselineRootTreeComponent = new BaseLineTreeComponent(tree);
bindAction('#baselineDestroyDom', baselineDestroyDom);
bindAction('#baselineCreateDom', baselineCreateDom);
bindAction('#baselineUpdateDomProfile', profile(baselineCreateDom, noop, 'baseline-update'));
bindAction('#baselineCreateDomProfile', profile(baselineCreateDom, baselineDestroyDom, 'baseline-create'));
}
initNg2();
initBaseline();
}
class TreeNode {
value:string;
left:TreeNode;
right:TreeNode;
constructor(value, left, right) {
this.value = value;
this.left = left;
this.right = right;
}
}
function buildTree(maxDepth, values, curDepth) {
if (maxDepth === curDepth) return new TreeNode('', null, null);
return new TreeNode(
values[curDepth],
buildTree(maxDepth, values, curDepth+1),
buildTree(maxDepth, values, curDepth+1));
}
// http://jsperf.com/nextsibling-vs-childnodes
class BaseLineTreeComponent {
element;
value:BaseLineInterpolation;
left:BaseLineIf;
right:BaseLineIf;
constructor(element) {
this.element = element;
var clone = DOM.clone(BASELINE_TREE_TEMPLATE.content.firstChild);
var shadowRoot = this.element.createShadowRoot();
DOM.appendChild(shadowRoot, clone);
var child = clone.firstChild;
this.value = new BaseLineInterpolation(child);
child = DOM.nextSibling(child);
this.left = new BaseLineIf(child);
child = DOM.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);
}
}
class BaseLineInterpolation {
value:string;
textNode;
constructor(textNode) {
this.value = null;
this.textNode = textNode;
}
update(value:string) {
if (this.value !== value) {
this.value = value;
DOM.setText(this.textNode, value + ' ');
}
}
}
class BaseLineIf {
condition:boolean;
component:BaseLineTreeComponent;
anchor;
constructor(anchor) {
this.anchor = anchor;
this.condition = false;
this.component = null;
}
update(value:TreeNode) {
var newCondition = isPresent(value);
if (this.condition !== newCondition) {
this.condition = newCondition;
if (isPresent(this.component)) {
DOM.remove(this.component.element);
this.component = null;
}
if (this.condition) {
var element = DOM.firstChild(DOM.clone(BASELINE_IF_TEMPLATE).content);
this.anchor.parentNode.insertBefore(element, DOM.nextSibling(this.anchor));
this.component = new BaseLineTreeComponent(DOM.firstChild(element));
}
}
if (isPresent(this.component)) {
this.component.update(value);
}
}
}
@Component({selector: 'app'})
@View({
directives: [TreeComponent],
template: `<tree [data]='initData'></tree>`
})
class AppComponent {
initData:TreeNode;
constructor() {
// TODO: We need an initial value as otherwise the getter for data.value will fail
// --> this should be already caught in change detection!
this.initData = new TreeNode('', null, null);
}
}
@Component({
selector: 'tree',
properties: {'data': 'data'}
})
@View({
directives: [TreeComponent, If],
template: `<span> {{data.value}} <span template='if data.right != null'><tree [data]='data.right'></tree></span><span template='if data.left != null'><tree [data]='data.left'></tree></span></span>`
})
class TreeComponent {
data:TreeNode;
}