diff --git a/.gitignore b/.gitignore index b577071b1e..42e9772f40 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ packages/ .buildlog node_modules packages - +.pub .DS_STORE # Or the files created by dart2js. diff --git a/gulpfile.js b/gulpfile.js index 1400dca6a0..efa5ae647e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -248,17 +248,31 @@ gulp.task('analyze/dartanalyzer', function(done) { // ------------------ // BENCHMARKS JS -gulp.task('benchmarks/build.benchpress.js', function () { +gulp.task('benchmarks/internal.benchpress.js', function () { benchpress.build({ benchmarksPath: 'build/js/benchmarks/lib', buildPath: 'build/benchpress/js' }) }); -gulp.task('benchmarks/build.js', function() { +gulp.task('benchmarks/external.benchpress.js', function () { + benchpress.build({ + benchmarksPath: 'build/js/benchmarks_external/lib', + buildPath: 'build/benchpress/js' + }) +}); + +gulp.task('benchmarks/internal.js', function() { runSequence( ['jsRuntime/build', 'modules/build.prod.js'], - 'benchmarks/build.benchpress.js' + 'benchmarks/internal.benchpress.js' + ); +}); + +gulp.task('benchmarks/external.js', function() { + runSequence( + ['jsRuntime/build', 'modules/build.prod.js'], + 'benchmarks/external.benchpress.js' ); }); @@ -266,40 +280,50 @@ gulp.task('benchmarks/build.js', function() { // ------------------ // BENCHMARKS DART -gulp.task('benchmarks/build.dart2js.dart', function () { - return gulp.src([ - "build/dart/benchmarks/lib/**/benchmark.dart" - ]).pipe($.shell(['dart2js --package-root="build/dart/benchmarks/packages" -o "<%= file.path %>.js" <%= file.path %>'])); +function benchmarkDart2Js(buildPath, done) { + + mergeStreams(dart2jsStream(), bpConfStream()) + .on('end', function() { + runBenchpress(); + done(); + }); + + function dart2jsStream() { + return gulp.src([ + buildPath+"/lib/**/benchmark.dart" + ]).pipe($.shell(['dart2js --package-root="'+buildPath+'/packages" -o "<%= file.path %>.js" <%= file.path %>'])); + } + + function bpConfStream() { + var bpConfContent = "module.exports = function(c) {c.set({scripts: [{src: 'benchmark.dart.js'}]});}"; + var createBpConfJs = es.map(function(file, cb) { + var dir = path.dirname(file.path); + fs.writeFileSync(path.join(dir, "bp.conf.js"), bpConfContent); + cb(); + }); + + return gulp.src([ + buildPath+"/lib/**/benchmark.dart" + ]).pipe(createBpConfJs); + } + + function runBenchpress() { + benchpress.build({ + benchmarksPath: buildPath+'/lib', + buildPath: 'build/benchpress/dart' + }); + } +} + +gulp.task('benchmarks/internal.dart', ['modules/build.dart'], function(done) { + benchmarkDart2Js('build/dart/benchmarks', done); }); -gulp.task('benchmarks/create-bpconf.dart', function () { - var bpConfContent = "module.exports = function(c) {c.set({scripts: [{src: 'benchmark.dart.js'}]});}"; - var createBpConfJs = es.map(function(file, cb) { - var dir = path.dirname(file.path); - fs.writeFileSync(path.join(dir, "bp.conf.js"), bpConfContent); - cb(); - }); - - return gulp.src([ - "build/dart/benchmarks/lib/**/benchmark.dart" - ]).pipe(createBpConfJs); +gulp.task('benchmarks/external.dart', ['modules/build.dart'], function(done) { + benchmarkDart2Js('build/dart/benchmarks_external', done); }); -gulp.task('benchmarks/build.benchpress.dart', function () { - benchpress.build({ - benchmarksPath: 'build/dart/benchmarks/lib', - buildPath: 'build/benchpress/dart' - }) -}); - -gulp.task('benchmarks/build.dart', function() { - runSequence( - 'modules/build.dart', - 'benchmarks/build.dart2js.dart', - 'benchmarks/create-bpconf.dart', - 'benchmarks/build.benchpress.dart' - ); -}); +gulp.task('benchmarks/build.dart', ['benchmarks/internal.dart', 'benchmarks/external.dart']); diff --git a/modules/benchmarks/pubspec.yaml b/modules/benchmarks/pubspec.yaml index a1dd3cb711..0e16a3ee09 100644 --- a/modules/benchmarks/pubspec.yaml +++ b/modules/benchmarks/pubspec.yaml @@ -6,6 +6,8 @@ dependencies: path: ../facade di: path: ../di + reflection: + path: ../reflection core: path: ../core change_detection: diff --git a/modules/benchmarks/src/compiler/benchmark.es5 b/modules/benchmarks/src/compiler/benchmark.es5 index 760118bd84..debc6af297 100644 --- a/modules/benchmarks/src/compiler/benchmark.es5 +++ b/modules/benchmarks/src/compiler/benchmark.es5 @@ -1,7 +1,6 @@ Promise.all([ System.import('benchmarks/compiler/selector_benchmark'), - System.import('benchmarks/compiler/compiler_benchmark'), - System.import('benchmarks/compiler/compiler_benchmark_ng13') + System.import('benchmarks/compiler/compiler_benchmark') ]).then(function (benchmarks) { benchmarks.forEach(function(bm) { bm.main(); diff --git a/modules/benchmarks/src/compiler/bp.conf.es5 b/modules/benchmarks/src/compiler/bp.conf.es5 index 235bb40d80..b9ff8a4015 100644 --- a/modules/benchmarks/src/compiler/bp.conf.es5 +++ b/modules/benchmarks/src/compiler/bp.conf.es5 @@ -5,7 +5,6 @@ module.exports = function(config) { {src: '/js/es6-module-loader-sans-promises.src.js'}, {src: '/js/extension-register.js'}, {src: 'register_system.js'}, - {src: 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.js'}, {src: 'benchmark.js'} ] }); diff --git a/modules/benchmarks/src/compiler/compiler_benchmark.js b/modules/benchmarks/src/compiler/compiler_benchmark.js index 33340461d3..4b1d401476 100644 --- a/modules/benchmarks/src/compiler/compiler_benchmark.js +++ b/modules/benchmarks/src/compiler/compiler_benchmark.js @@ -7,6 +7,7 @@ import {AnnotatedType} from 'core/compiler/annotated_type'; import {Parser} from 'change_detection/parser/parser'; import {Lexer} from 'change_detection/parser/lexer'; +import {ProtoRecordRange} from 'change_detection/record_range'; import {Compiler} from 'core/compiler/compiler'; import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader'; @@ -15,12 +16,70 @@ import {Component} from 'core/annotations/annotations'; import {Decorator} from 'core/annotations/annotations'; import {TemplateConfig} from 'core/annotations/template_config'; +import {reflector} from 'reflection/reflection'; + var COUNT = 30; var compiler; var annotatedComponent; function setup() { + reflector.registerType(BenchmarkComponent, { + "factory": () => new BenchmarkComponent(), + "parameters": [], + "annotations" : [new Component({template: new TemplateConfig({directives: [Dir0, Dir1, Dir2, Dir3, Dir4]})})] + }); + + reflector.registerType(Dir0, { + "factory": () => new Dir0(), + "parameters": [], + "annotations" : [new Decorator({selector: '[dir0]', bind: {'attr0': 'prop'}})] + }); + + reflector.registerType(Dir1, { + "factory": (dir0) => new Dir1(dir0), + "parameters": [[Dir0]], + "annotations" : [new Decorator({selector: '[dir1]', bind: {'attr1': 'prop'}})] + }); + + reflector.registerType(Dir2, { + "factory": (dir1) => new Dir2(dir1), + "parameters": [[Dir1]], + "annotations" : [new Decorator({selector: '[dir2]', bind: {'attr2': 'prop'}})] + }); + + reflector.registerType(Dir3, { + "factory": (dir2) => new Dir3(dir2), + "parameters": [[Dir2]], + "annotations" : [new Decorator({selector: '[dir3]', bind: {'attr3': 'prop'}})] + }); + + reflector.registerType(Dir4, { + "factory": (dir3) => new Dir4(dir3), + "parameters": [[Dir3]], + "annotations" : [new Decorator({selector: '[dir4]', bind: {'attr4': 'prop'}})] + }); + + reflector.registerGetters({ + "inter0": (a) => a.inter0, "inter1": (a) => a.inter1, + "inter2": (a) => a.inter2, "inter3": (a) => a.inter3, "inter4": (a) => a.inter4, + + "value0": (a) => a.value0, "value1": (a) => a.value1, + "value2": (a) => a.value2, "value3": (a) => a.value3, "value4": (a) => a.value4, + + "prop" : (a) => a.prop + }); + + reflector.registerSetters({ + "inter0": (a,v) => a.inter0 = v, "inter1": (a,v) => a.inter1 = v, + "inter2": (a,v) => a.inter2 = v, "inter3": (a,v) => a.inter3 = v, "inter4": (a,v) => a.inter4 = v, + + "value0": (a,v) => a.value0 = v, "value1": (a,v) => a.value1 = v, + "value2": (a,v) => a.value2 = v, "value3": (a,v) => a.value3 = v, "value4": (a,v) => a.value4 = v, + + "prop": (a,v) => a.prop = v + }); + var reader = new CachingDirectiveMetadataReader(); compiler = new Compiler(null, reader, new Parser(new Lexer())); annotatedComponent = reader.annotatedType(BenchmarkComponent); @@ -48,6 +107,19 @@ export function main() { compiler.compileWithCache(null, annotatedComponent, cloned); }); }); + + benchmark(`instantiate 5*${COUNT} element with bindings`, function() { + var template = loadTemplate('templateWithBindings', COUNT); + var protoView = compiler.compileWithCache(null, annotatedComponent, template); + var rootRecordRange = new ProtoRecordRange().instantiate(null, new Object()); + + benchmarkStep('run', function() { + var view = protoView.instantiate(null, null, null); + // also include adding / removing the RecordRange from the parent in the benchmark. + rootRecordRange.addRange(view.recordRange); + view.recordRange.remove(); + }); + }); } function loadTemplate(templateId, repeatCount) { @@ -90,7 +162,9 @@ class Dir0 {} 'attr1': 'prop' } }) -class Dir1 {} +class Dir1 { + constructor(dir0:Dir0) {} +} @Decorator({ selector: '[dir2]', @@ -98,7 +172,9 @@ class Dir1 {} 'attr2': 'prop' } }) -class Dir2 {} +class Dir2 { + constructor(dir1:Dir1) {} +} @Decorator({ selector: '[dir3]', @@ -106,7 +182,9 @@ class Dir2 {} 'attr3': 'prop' } }) -class Dir3 {} +class Dir3 { + constructor(dir2:Dir2) {} +} @Decorator({ selector: '[dir4]', @@ -114,7 +192,9 @@ class Dir3 {} 'attr4': 'prop' } }) -class Dir4 {} +class Dir4 { + constructor(dir3:Dir3) {} +} @Component({ template: new TemplateConfig({ diff --git a/modules/benchmarks_external/pubspec.yaml b/modules/benchmarks_external/pubspec.yaml new file mode 100644 index 0000000000..0678203497 --- /dev/null +++ b/modules/benchmarks_external/pubspec.yaml @@ -0,0 +1,5 @@ +name: benchmarks_external +environment: + sdk: '>=1.4.0' +dependencies: + angular: ">=1.0.0 <2.0.0" diff --git a/modules/benchmarks_external/src/benchpress.dart b/modules/benchmarks_external/src/benchpress.dart new file mode 100644 index 0000000000..81542709bd --- /dev/null +++ b/modules/benchmarks_external/src/benchpress.dart @@ -0,0 +1,63 @@ +library benchmarks.benchpress; + +import 'dart:js' as js; +import 'dart:html'; +import 'dart:async'; + +// TODO: move the functionality of this module into benchpress and replace this +// file with a Dart wrapper! + +var _benchmarkNames = []; + +_benchmarkId(index) { + return "benchmark${index}"; +} + +_useBenchmark(index) { + var search = window.location.search; + if (search.length > 0) { + search = search.substring(1); + } + if (search.length > 0) { + return search == _benchmarkId(index); + } else { + return true; + } +} + +_onLoad(callback) { + var isReady = document.readyState == 'complete'; + if (isReady) { + Timer.run(callback); + } else { + window.addEventListener('load', (event) => callback(), false); + } +} + +_createBenchmarkMenu() { + var div = document.createElement('div'); + div.innerHtml += '

Benchmarks:

All'; + for (var i=0; i<_benchmarkNames.length; i++) { + var activeClass = _useBenchmark(i) ? 'active' : ''; + div.innerHtml += '${_benchmarkNames[i]}'; + } + document.body.insertBefore(div, document.body.childNodes[0]); +} + +benchmark(name, stepsCreationCallback) { + _benchmarkNames.add(name); + if (_benchmarkNames.length == 2) { + _onLoad(_createBenchmarkMenu); + } + if (_useBenchmark(_benchmarkNames.length-1)) { + stepsCreationCallback(); + } +} + +benchmarkStep(name, callback) { + var benchmarkName = _benchmarkNames[_benchmarkNames.length-1]; + js.context['benchmarkSteps'].add(new js.JsObject.jsify({ + "name": benchmarkName + '#' + name, + "fn": new js.JsFunction.withThis((_) => callback()) + })); +} diff --git a/modules/benchmarks_external/src/benchpress.es6 b/modules/benchmarks_external/src/benchpress.es6 new file mode 100644 index 0000000000..d5e5d5e0df --- /dev/null +++ b/modules/benchmarks_external/src/benchpress.es6 @@ -0,0 +1,55 @@ +// TODO: move the functionality of this module into benchpress itself! + +var benchmarkNames = []; + +function benchmarkId(index) { + return 'benchmark' + index; +} + +function useBenchmark(index) { + var search = window.location.search; + if (search.length > 0) { + search = search.substring(1); + } + if (search.length > 0) { + return search == benchmarkId(index); + } else { + return true; + } +} + +function onLoad(callback) { + var isReady = document.readyState === 'complete'; + if (isReady) { + window.setTimeout(callback); + } else { + window.addEventListener('load', callback, false); + } +} + +function createBenchmarkMenu() { + var div = document.createElement('div'); + div.innerHTML += '

Benchmarks:

All'; + for (var i=0; i'+benchmarkNames[i]+''); + } + document.body.insertBefore(div, document.body.childNodes[0]); +} + +export function benchmark(name, stepsCreationCallback) { + benchmarkNames.push(name); + if (benchmarkNames.length === 2) { + onLoad(createBenchmarkMenu); + } + if (useBenchmark(benchmarkNames.length-1)) { + stepsCreationCallback(); + } +} + +export function benchmarkStep(name, callback) { + var benchmarkName = benchmarkNames[benchmarkNames.length-1]; + window.benchmarkSteps.push({ + name: benchmarkName + '#' + name, fn: callback + }); +} diff --git a/modules/benchmarks_external/src/compiler/benchmark.dart b/modules/benchmarks_external/src/compiler/benchmark.dart new file mode 100644 index 0000000000..8c6ceb28a2 --- /dev/null +++ b/modules/benchmarks_external/src/compiler/benchmark.dart @@ -0,0 +1,7 @@ +library compiler_benchmark; + +import './compiler_benchmark_ng10.dart' as cbm; + +main () { + cbm.main(); +} \ No newline at end of file diff --git a/modules/benchmarks_external/src/compiler/benchmark.es5 b/modules/benchmarks_external/src/compiler/benchmark.es5 new file mode 100644 index 0000000000..ff4715023c --- /dev/null +++ b/modules/benchmarks_external/src/compiler/benchmark.es5 @@ -0,0 +1,7 @@ +Promise.all([ + System.import('benchmarks_external/compiler/compiler_benchmark_ng13') +]).then(function (benchmarks) { + benchmarks.forEach(function(bm) { + bm.main(); + }); +}, console.log.bind(console)); diff --git a/modules/benchmarks_external/src/compiler/bp.conf.es5 b/modules/benchmarks_external/src/compiler/bp.conf.es5 new file mode 100644 index 0000000000..235bb40d80 --- /dev/null +++ b/modules/benchmarks_external/src/compiler/bp.conf.es5 @@ -0,0 +1,12 @@ +module.exports = function(config) { + config.set({ + scripts: [ + {src: '/js/traceur-runtime.js'}, + {src: '/js/es6-module-loader-sans-promises.src.js'}, + {src: '/js/extension-register.js'}, + {src: 'register_system.js'}, + {src: 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.js'}, + {src: 'benchmark.js'} + ] + }); +}; diff --git a/modules/benchmarks_external/src/compiler/compiler_benchmark_ng10.dart b/modules/benchmarks_external/src/compiler/compiler_benchmark_ng10.dart new file mode 100644 index 0000000000..8aee93e026 --- /dev/null +++ b/modules/benchmarks_external/src/compiler/compiler_benchmark_ng10.dart @@ -0,0 +1,175 @@ +import 'package:angular/angular.dart'; +import 'package:angular/application_factory_static.dart'; +import '../benchpress.dart'; +import 'dart:html'; + +var COUNT = 30; + +main() { + + final typeAnnotations = { + Dir0: const [const Decorator(selector: '[dir0]', map: const {'attr0': '=>prop'})], + Dir1: const [const Decorator(selector: '[dir1]', map: const {'attr1': '=>prop'})], + Dir2: const [const Decorator(selector: '[dir2]', map: const {'attr2': '=>prop'})], + Dir3: const [const Decorator(selector: '[dir3]', map: const {'attr3': '=>prop'})], + Dir4: const [const Decorator(selector: '[dir4]', map: const {'attr4': '=>prop'})] + }; + + final fieldGetters = { + "inter0": (a) => a.inter0, "inter1": (a) => a.inter1, + "inter2": (a) => a.inter2, "inter3": (a) => a.inter3, "inter4": (a) => a.inter4, + + "value0": (a) => a.value0, "value1": (a) => a.value1, + "value2": (a) => a.value2, "value3": (a) => a.value3, "value4": (a) => a.value4, + + "prop" : (a) => a.prop + }; + + final fieldSetters = { + "inter0": (a,v) => a.inter0 = v, "inter1": (a,v) => a.inter1 = v, + "inter2": (a,v) => a.inter2 = v, "inter3": (a,v) => a.inter3 = v, "inter4": (a,v) => a.inter4 = v, + + "value0": (a,v) => a.value0 = v, "value1": (a,v) => a.value1 = v, + "value2": (a,v) => a.value2 = v, "value3": (a,v) => a.value3 = v, "value4": (a,v) => a.value4 = v, + + "prop": (a,v) => a.prop = v + }; + + final symbols = { + }; + + var m = new Module() + ..bind(Dir0) + ..bind(Dir1) + ..bind(Dir2) + ..bind(Dir3) + ..bind(Dir4); + + benchmark("AngularDart 1.0 Compiler.compile 5*${COUNT} element with bindings", () { + var template = loadTemplate('templateWithBindings', COUNT); + + final injector = staticApplicationFactory( + typeAnnotations, + fieldGetters, + fieldSetters, + symbols + ).addModule(m).run(); + + final compiler = injector.get(Compiler); + final directiveMap = injector.get(DirectiveMap); + final di = injector.get(DirectiveInjector); + final rootScope = injector.get(Scope); + + benchmarkStep('run', () { + final cloned = template.clone(true); + final scope = rootScope.createChild({}); + final viewFactory = compiler([cloned], directiveMap); + viewFactory(scope, di); + scope.destroy(); + }); + }); + + benchmark("AngularDart 1.0 instantiate 5*${COUNT} element with bindings", () { + var template = loadTemplate('templateWithBindings', COUNT); + + final injector = staticApplicationFactory( + typeAnnotations, + fieldGetters, + fieldSetters, + symbols + ).addModule(m).run(); + + final compiler = injector.get(Compiler); + final directiveMap = injector.get(DirectiveMap); + final di = injector.get(DirectiveInjector); + final rootScope = injector.get(Scope); + final viewFactory = compiler([template], directiveMap); + + benchmarkStep('run', () { + var scope = rootScope.createChild({}); + viewFactory(scope, di); + scope.destroy(); + }); + }); +} + +loadTemplate(templateId, repeatCount) { + String result = ''; + var content = document.querySelector("#${templateId}").innerHtml; + for (var i=0; iprop' + } +) +class Dir0 { + Object prop; +} + +@Decorator( + selector: '[dir1]', + map: const { + 'attr1': '=>prop' + } +) +class Dir1 { + Object prop; + + constructor(Dir0 dir0) { + } +} + +@Decorator( + selector: '[dir2]', + map: const { + 'attr2': '=>prop' + } +) +class Dir2 { + Object prop; + + constructor(Dir1 dir1) { + } +} + +@Decorator( + selector: '[dir3]', + map: const { + 'attr3': '=>prop' + } +) +class Dir3 { + Object prop; + + constructor(Dir2 dir2) { + } +} + +@Decorator( + selector: '[dir4]', + map: const { + 'attr4': '=>prop' + } +) +class Dir4 { + Object prop; + + constructor(Dir3 dir3) { + } +} \ No newline at end of file diff --git a/modules/benchmarks/src/compiler/compiler_benchmark_ng13.es6 b/modules/benchmarks_external/src/compiler/compiler_benchmark_ng13.es6 similarity index 84% rename from modules/benchmarks/src/compiler/compiler_benchmark_ng13.es6 rename to modules/benchmarks_external/src/compiler/compiler_benchmark_ng13.es6 index e4b2a4db19..1de20ff6e9 100644 --- a/modules/benchmarks/src/compiler/compiler_benchmark_ng13.es6 +++ b/modules/benchmarks_external/src/compiler/compiler_benchmark_ng13.es6 @@ -2,6 +2,7 @@ import {benchmark, benchmarkStep} from '../benchpress'; var COUNT = 30; var $compile; +var $rootScope; export function main() { @@ -25,6 +26,21 @@ export function main() { }); }); + benchmark(`Ng 1.3 instantiate 5*${COUNT} element with bindings`, function() { + var linkFn; + + setTimeout(function() { + var template = loadTemplate('templateWithBindings', COUNT); + linkFn = $compile(template); + }); + + benchmarkStep('run', function() { + var scope = $rootScope.$new(); + linkFn(scope); + scope.$destroy(); + }); + }); + var ngEl = document.createElement('div'); angular.bootstrap(ngEl, ['app']); } @@ -96,7 +112,8 @@ angular.module('app', []) } }; }) -.run(function(_$compile_) { +.run(function(_$compile_, _$rootScope_) { $compile = _$compile_; + $rootScope = _$rootScope_; }); diff --git a/modules/benchmarks_external/src/compiler/main.html b/modules/benchmarks_external/src/compiler/main.html new file mode 100644 index 0000000000..d16b04e8fa --- /dev/null +++ b/modules/benchmarks_external/src/compiler/main.html @@ -0,0 +1,30 @@ + + + diff --git a/modules/benchmarks_external/src/compiler/register_system.es5 b/modules/benchmarks_external/src/compiler/register_system.es5 new file mode 100644 index 0000000000..34499cfd2b --- /dev/null +++ b/modules/benchmarks_external/src/compiler/register_system.es5 @@ -0,0 +1,11 @@ +System.paths = { + 'core/*': '/js/core/lib/*.js', + 'change_detection/*': '/js/change_detection/lib/*.js', + 'facade/*': '/js/facade/lib/*.js', + 'di/*': '/js/di/lib/*.js', + 'rtts_assert/*': '/js/rtts_assert/lib/*.js', + 'test_lib/*': '/js/test_lib/lib/*.js', + 'benchmarks_external/*': '/js/benchmarks_external/lib/*.js', + 'reflection/*': '/js/reflection/lib/*.js' +}; +register(System); diff --git a/scripts/perf_launch_chrome.sh b/scripts/perf_launch_chrome.sh new file mode 100644 index 0000000000..913e32d1df --- /dev/null +++ b/scripts/perf_launch_chrome.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +platform=`uname` +if [[ "$platform" == 'Linux' ]]; then + `google-chrome --js-flags="--expose-gc"` +elif [[ "$platform" == 'Darwin' ]]; then + `/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \ + --enable-memory-info \ + --enable-precise-memory-info \ + --enable-memory-benchmarking \ + --js-flags="--expose-gc" \ + --remote-debugging-port=9222` +fi \ No newline at end of file