diff --git a/modules/benchmarks/src/tree/main.html b/modules/benchmarks/src/tree/main.html
new file mode 100644
index 0000000000..901c3dbe1f
--- /dev/null
+++ b/modules/benchmarks/src/tree/main.html
@@ -0,0 +1,3 @@
+$SCRIPTS$
+
+
\ No newline at end of file
diff --git a/modules/benchmarks/src/tree/main.js b/modules/benchmarks/src/tree/main.js
new file mode 100644
index 0000000000..f98bafc9a3
--- /dev/null
+++ b/modules/benchmarks/src/tree/main.js
@@ -0,0 +1,5 @@
+import * as tree_benchmark from './tree_benchmark';
+
+export function main() {
+ tree_benchmark.main();
+}
diff --git a/modules/benchmarks/src/tree/tree_benchmark.js b/modules/benchmarks/src/tree/tree_benchmark.js
new file mode 100644
index 0000000000..8f416072d0
--- /dev/null
+++ b/modules/benchmarks/src/tree/tree_benchmark.js
@@ -0,0 +1,198 @@
+import {benchmark, benchmarkStep} from 'benchpress/benchpress';
+
+import {ChangeDetector} from 'change_detection/change_detector';
+import {Parser} from 'change_detection/parser/parser';
+import {Lexer} from 'change_detection/parser/lexer';
+
+import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler} from 'core/core';
+
+import {CompilerCache} from 'core/compiler/compiler';
+import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
+import {TemplateLoader} from 'core/compiler/template_loader';
+
+import {reflector} from 'reflection/reflection';
+
+var MAX_DEPTH = 9;
+
+function setup() {
+ // TODO: Put the general calls to reflector.register... in a shared file
+ // as they are needed in all benchmarks...
+
+ reflector.registerType(AppComponent, {
+ 'factory': () => new AppComponent(),
+ 'parameters': [],
+ 'annotations' : [new Component({
+ selector: 'app',
+ template: new TemplateConfig({
+ directives: [TreeComponent],
+ inline: ``
+ })
+ })]
+ });
+
+ reflector.registerType(TreeComponent, {
+ 'factory': () => new TreeComponent(),
+ 'parameters': [],
+ 'annotations' : [new Component({
+ selector: 'tree',
+ bind: {
+ 'data': 'data'
+ },
+ template: new TemplateConfig({
+ directives: [TreeComponent, NgIf],
+ inline: `
+ {{data.value}}
+
+
+ `
+ })
+ })]
+ });
+
+ reflector.registerType(NgIf, {
+ 'factory': (vp) => new NgIf(vp),
+ 'parameters': [[ViewPort]],
+ 'annotations' : [new Template({
+ selector: '[ng-if]',
+ bind: {
+ 'ng-if': 'ngIf'
+ }
+ })]
+ });
+
+ reflector.registerType(Compiler, {
+ 'factory': (templateLoader, reader, parser, compilerCache) => new Compiler(templateLoader, reader, parser, compilerCache),
+ 'parameters': [[TemplateLoader], [DirectiveMetadataReader], [Parser], [CompilerCache]],
+ 'annotations': []
+ });
+
+ reflector.registerType(CompilerCache, {
+ 'factory': () => new CompilerCache(),
+ 'parameters': [],
+ 'annotations': []
+ });
+
+ reflector.registerType(Parser, {
+ 'factory': (lexer) => new Parser(lexer),
+ 'parameters': [[Lexer]],
+ 'annotations': []
+ });
+
+ reflector.registerType(TemplateLoader, {
+ 'factory': () => new TemplateLoader(),
+ 'parameters': [],
+ 'annotations': []
+ });
+
+ reflector.registerType(DirectiveMetadataReader, {
+ 'factory': () => new DirectiveMetadataReader(),
+ 'parameters': [],
+ 'annotations': []
+ });
+
+ reflector.registerType(Lexer, {
+ 'factory': () => new Lexer(),
+ 'parameters': [],
+ 'annotations': []
+ });
+
+
+ reflector.registerGetters({
+ 'value': (a) => a.value,
+ 'left': (a) => a.left,
+ 'right': (a) => a.right,
+ 'initData': (a) => a.initData,
+ 'data': (a) => a.data
+ });
+
+ reflector.registerSetters({
+ 'value': (a,v) => a.value = v,
+ 'left': (a,v) => a.left = v,
+ 'right': (a,v) => a.right = v,
+ 'initData': (a,v) => a.initData = v,
+ 'data': (a,v) => a.data = v,
+ 'ngIf': (a,v) => a.ngIf = v
+ });
+
+ return bootstrap(AppComponent);
+}
+
+export function main() {
+ var app;
+ var changeDetector;
+ setup().then((injector) => {
+ changeDetector = injector.get(ChangeDetector);
+ app = injector.get(AppComponent);
+ });
+
+ benchmark(`tree benchmark`, function() {
+ var count = 0;
+
+ benchmarkStep(`destroyDom binary tree of depth ${MAX_DEPTH}`, function() {
+ // 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);
+ changeDetector.detectChanges();
+ });
+
+ benchmarkStep(`createDom binary tree of depth ${MAX_DEPTH}`, function() {
+ var maxDepth = 9;
+ 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);
+ changeDetector.detectChanges();
+ });
+
+ });
+}
+
+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));
+}
+
+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);
+ }
+}
+
+// TODO: Move this into a reusable directive in the 'core' module!
+class NgIf {
+ _viewPort:ViewPort;
+ constructor(viewPort:ViewPort) {
+ this._viewPort = viewPort;
+ }
+ set ngIf(value:boolean) {
+ if (this._viewPort.length > 0) {
+ this._viewPort.remove(0);
+ }
+ if (value) {
+ this._viewPort.create();
+ }
+ }
+}
+
+class TreeComponent {
+ data:TreeNode;
+}
+
diff --git a/modules/benchmarks_external/src/tree/main.dart b/modules/benchmarks_external/src/tree/main.dart
new file mode 100644
index 0000000000..59d88c0aee
--- /dev/null
+++ b/modules/benchmarks_external/src/tree/main.dart
@@ -0,0 +1,7 @@
+library tree_benchmark;
+
+import './tree_benchmark_ng10.dart' as bm;
+
+main () {
+ bm.main();
+}
\ No newline at end of file
diff --git a/modules/benchmarks_external/src/tree/main.es6 b/modules/benchmarks_external/src/tree/main.es6
new file mode 100644
index 0000000000..c064dd1dbb
--- /dev/null
+++ b/modules/benchmarks_external/src/tree/main.es6
@@ -0,0 +1 @@
+export {main} from './tree_benchmark_ng13';
diff --git a/modules/benchmarks_external/src/tree/main.html b/modules/benchmarks_external/src/tree/main.html
new file mode 100644
index 0000000000..a260a6be0f
--- /dev/null
+++ b/modules/benchmarks_external/src/tree/main.html
@@ -0,0 +1,3 @@
+$SCRIPTS$
+
+
\ No newline at end of file
diff --git a/modules/benchmarks_external/src/tree/tree_benchmark_ng10.dart b/modules/benchmarks_external/src/tree/tree_benchmark_ng10.dart
new file mode 100644
index 0000000000..44a651e4dd
--- /dev/null
+++ b/modules/benchmarks_external/src/tree/tree_benchmark_ng10.dart
@@ -0,0 +1,76 @@
+library tree_benchmark_ng10;
+
+import 'package:angular/angular.dart';
+import 'package:angular/application_factory.dart';
+import 'package:benchpress/benchpress.dart';
+import 'dart:html';
+
+var MAX_DEPTH = 9;
+
+setup() {
+
+ var m = new Module()
+ ..bind(CompilerConfig, toValue: new CompilerConfig.withOptions(elementProbeEnabled: false))
+ ..bind(ScopeDigestTTL, toFactory: () => new ScopeDigestTTL.value(15), inject: [])
+ ..bind(TreeComponent);
+
+ final injector = applicationFactory().addModule(m).run();
+
+ return injector;
+}
+
+main() {
+ final injector = setup();
+ final zone = injector.get(VmTurnZone);
+ final rootScope = injector.get(Scope);
+
+ benchmark("tree benchmark", () {
+ var count = 0;
+
+ benchmarkStep("AngularDart destroyDom binary tree of depth ${MAX_DEPTH}", () {
+ zone.run(() {
+ rootScope.context['initData'] = new TreeNode('');
+ });
+ });
+
+ benchmarkStep("AngularDart createDom binary tree of depth ${MAX_DEPTH}", () {
+ zone.run(() {
+ var maxDepth = 9;
+ 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', '-'];
+
+ rootScope.context['initData'] = buildTree(maxDepth, values, 0);
+ });
+ });
+
+ });
+}
+
+@Component(
+ selector: 'tree',
+ map: const {'data': '=>data'},
+ template: ' {{data.value}}'
+ ''
+ ''
+ ''
+)
+class TreeComponent {
+ var data;
+}
+
+buildTree(maxDepth, values, curDepth) {
+ if (maxDepth == curDepth) return new TreeNode('');
+ return new TreeNode(
+ values[curDepth],
+ buildTree(maxDepth, values, curDepth+1),
+ buildTree(maxDepth, values, curDepth+1));
+}
+
+class TreeNode {
+ var value;
+ TreeNode left;
+ TreeNode right;
+ TreeNode([this.value, this.left, this.right]);
+}
+
diff --git a/modules/benchmarks_external/src/tree/tree_benchmark_ng13.es6 b/modules/benchmarks_external/src/tree/tree_benchmark_ng13.es6
new file mode 100644
index 0000000000..9dd2c8af11
--- /dev/null
+++ b/modules/benchmarks_external/src/tree/tree_benchmark_ng13.es6
@@ -0,0 +1,112 @@
+import {benchmark, benchmarkStep} from 'benchpress/benchpress';
+
+var MAX_DEPTH = 9;
+
+function setup() {
+ var $rootScope;
+
+ angular.module('app', [])
+ .directive('tree', function() {
+ return {
+ scope: {
+ data: '='
+ },
+ template:
+' {{data.value}}'+
+' '+
+' '+
+''
+ };
+ })
+ // special directive for "if" as angular 1.3 does not support
+ // recursive components.
+ .directive('treeIf', ['$compile', '$parse', function($compile, $parse) {
+ var transcludeFn;
+ return {
+ compile: function(element, attrs) {
+ var expr = $parse(attrs.treeIf);
+ var template = '';
+ var transclude;
+ return function($scope, $element, $attrs) {
+ if (!transclude) {
+ transclude = $compile(template);
+ }
+ var childScope;
+ var childElement;
+ $scope.$watch(expr, function(newValue) {
+ if (childScope) {
+ childScope.$destroy();
+ childElement.remove();
+ childScope = null;
+ childElement = null;
+ }
+ if (newValue) {
+ childScope = $scope.$new();
+ childElement = transclude(childScope, function(clone) {
+ $element.append(clone);
+ });
+ }
+ });
+ }
+
+ }
+ }
+ }])
+ .config(['$compileProvider', function($compileProvider) {
+ $compileProvider.debugInfoEnabled(false);
+ }])
+ .run(['$rootScope', function(_$rootScope_) {
+ $rootScope = _$rootScope_;
+ }])
+ angular.bootstrap(document.body, ['app']);
+ return $rootScope;
+}
+
+export function main() {
+ var $rootScope = setup();
+
+ benchmark(`tree benchmark`, function() {
+ var count = 0;
+
+ benchmarkStep(`AngularJS destroyDom binary tree of depth ${MAX_DEPTH}`, function() {
+ $rootScope.$apply(function() {
+ $rootScope.initData = new TreeNode('', null, null);
+ });
+ });
+
+ benchmarkStep(`AngularJS createDom binary tree of depth ${MAX_DEPTH}`, function() {
+ var maxDepth = 9;
+ 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', '-'];
+
+ $rootScope.$apply(function() {
+ $rootScope.initData = buildTree(maxDepth, values, 0);
+ });
+ });
+
+ });
+
+}
+
+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));
+}
+
+
+