From 01fa90c11d35ceab12cce808b1f2c86cbc66ee64 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 8 Dec 2014 10:57:09 -0800 Subject: [PATCH] feat(benchmarks): initial version of tree benchmark Closes #269 --- modules/benchmarks/src/tree/main.html | 3 + modules/benchmarks/src/tree/main.js | 5 + modules/benchmarks/src/tree/tree_benchmark.js | 198 ++++++++++++++++++ .../benchmarks_external/src/tree/main.dart | 7 + modules/benchmarks_external/src/tree/main.es6 | 1 + .../benchmarks_external/src/tree/main.html | 3 + .../src/tree/tree_benchmark_ng10.dart | 76 +++++++ .../src/tree/tree_benchmark_ng13.es6 | 112 ++++++++++ 8 files changed, 405 insertions(+) create mode 100644 modules/benchmarks/src/tree/main.html create mode 100644 modules/benchmarks/src/tree/main.js create mode 100644 modules/benchmarks/src/tree/tree_benchmark.js create mode 100644 modules/benchmarks_external/src/tree/main.dart create mode 100644 modules/benchmarks_external/src/tree/main.es6 create mode 100644 modules/benchmarks_external/src/tree/main.html create mode 100644 modules/benchmarks_external/src/tree/tree_benchmark_ng10.dart create mode 100644 modules/benchmarks_external/src/tree/tree_benchmark_ng13.es6 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)); +} + + +