benchmark(compiler): add a benchmark measuring view instantiation in AngularDart 1.0

This commit is contained in:
vsavkin 2014-11-17 20:47:05 -08:00
parent c6f14dd833
commit 6187b80ce6
17 changed files with 541 additions and 42 deletions

2
.gitignore vendored
View File

@ -5,7 +5,7 @@ packages/
.buildlog
node_modules
packages
.pub
.DS_STORE
# Or the files created by dart2js.

View File

@ -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']);

View File

@ -6,6 +6,8 @@ dependencies:
path: ../facade
di:
path: ../di
reflection:
path: ../reflection
core:
path: ../core
change_detection:

View File

@ -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();

View File

@ -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'}
]
});

View File

@ -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({

View File

@ -0,0 +1,5 @@
name: benchmarks_external
environment:
sdk: '>=1.4.0'
dependencies:
angular: ">=1.0.0 <2.0.0"

View File

@ -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 += '<h1>Benchmarks:</h1><a class="btn btn-default" href="?">All</a>';
for (var i=0; i<_benchmarkNames.length; i++) {
var activeClass = _useBenchmark(i) ? 'active' : '';
div.innerHtml += '<a class="btn btn-default ${activeClass}" href="?${_benchmarkId(i)}">${_benchmarkNames[i]}</a>';
}
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())
}));
}

View File

@ -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 += '<h1>Benchmarks:</h1><a class="btn btn-default" href="?">All</a>';
for (var i=0; i<benchmarkNames.length; i++) {
var activeClass = useBenchmark(i) ? 'active' : '';
div.innerHTML += ('<a class="btn btn-default '+activeClass+'" href="?'+benchmarkId(i)+'">'+benchmarkNames[i]+'</a>');
}
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
});
}

View File

@ -0,0 +1,7 @@
library compiler_benchmark;
import './compiler_benchmark_ng10.dart' as cbm;
main () {
cbm.main();
}

View File

@ -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));

View File

@ -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'}
]
});
};

View File

@ -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; i<repeatCount; i++) {
result += content;
}
return createTemplate(result.replaceAll(new RegExp(r'[\[\]]'), ''));
}
class IdentitySanitizer implements NodeTreeSanitizer {
void sanitizeTree(Node node) {}
}
createTemplate(String html) {
var div = document.createElement('div');
div.setInnerHtml(html, treeSanitizer:new IdentitySanitizer());
return div;
}
@Decorator(
selector: '[dir0]',
map: const {
'attr0': '=>prop'
}
)
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) {
}
}

View File

@ -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_;
});

View File

@ -0,0 +1,30 @@
<template id="templateNoBindings">
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
<div class="class0 class1 class2 class3 class4 " nodir0="" attr0="value0" nodir1="" attr1="value1" nodir2="" attr2="value2" nodir3="" attr3="value3" nodir4="" attr4="value4">
</div>
</div>
</div>
</div>
</div>
</template>
<template id="templateWithBindings">
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
{{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
{{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
{{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
{{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
<div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
{{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
</div>
</div>
</div>
</div>
</div>
</template>

View File

@ -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);

View File

@ -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