Skip to content

Commit 8dfbc24

Browse files
committed
refactor(benchmark): simplify writing benchmarks
Adds `benchmarks/benchpress` module and adjusts the compiler benchmarks to use it. Also adds the Angular 1.3 benchmark to the compiler benchmarks. Closes angular#202
1 parent be4cb2d commit 8dfbc24

File tree

13 files changed

+365
-194
lines changed

13 files changed

+365
-194
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
library benchmarks.benchpress;
2+
3+
import 'dart:js' as js;
4+
import 'dart:html';
5+
import 'dart:async';
6+
7+
// TODO: move the functionality of this module into benchpress and replace this
8+
// file with a Dart wrapper!
9+
10+
var _benchmarkNames = [];
11+
12+
_benchmarkId(index) {
13+
return "benchmark${index}";
14+
}
15+
16+
_useBenchmark(index) {
17+
var search = window.location.search;
18+
if (search.length > 0) {
19+
search = search.substring(1);
20+
}
21+
if (search.length > 0) {
22+
return search == _benchmarkId(index);
23+
} else {
24+
return true;
25+
}
26+
}
27+
28+
_onLoad(callback) {
29+
var isReady = document.readyState == 'complete';
30+
if (isReady) {
31+
Timer.run(callback);
32+
} else {
33+
window.addEventListener('load', (event) => callback(), false);
34+
}
35+
}
36+
37+
_createBenchmarkMenu() {
38+
var div = document.createElement('div');
39+
div.innerHtml += '<h1>Benchmarks:</h1><a class="btn btn-default" href="?">All</a>';
40+
for (var i=0; i<_benchmarkNames.length; i++) {
41+
var activeClass = _useBenchmark(i) ? 'active' : '';
42+
div.innerHtml += '<a class="btn btn-default ${activeClass}" href="?${_benchmarkId(i)}">${_benchmarkNames[i]}</a>';
43+
}
44+
document.body.insertBefore(div, document.body.childNodes[0]);
45+
}
46+
47+
benchmark(name, stepsCreationCallback) {
48+
_benchmarkNames.add(name);
49+
if (_benchmarkNames.length == 2) {
50+
_onLoad(_createBenchmarkMenu);
51+
}
52+
if (_useBenchmark(_benchmarkNames.length-1)) {
53+
stepsCreationCallback();
54+
}
55+
}
56+
57+
benchmarkStep(name, callback) {
58+
var benchmarkName = _benchmarkNames[_benchmarkNames.length-1];
59+
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
60+
"name": benchmarkName + '#' + name,
61+
"fn": new js.JsFunction.withThis((_) => callback())
62+
}));
63+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// TODO: move the functionality of this module into benchpress itself!
2+
3+
var benchmarkNames = [];
4+
5+
function benchmarkId(index) {
6+
return 'benchmark' + index;
7+
}
8+
9+
function useBenchmark(index) {
10+
var search = window.location.search;
11+
if (search.length > 0) {
12+
search = search.substring(1);
13+
}
14+
if (search.length > 0) {
15+
return search == benchmarkId(index);
16+
} else {
17+
return true;
18+
}
19+
}
20+
21+
function onLoad(callback) {
22+
var isReady = document.readyState === 'complete';
23+
if (isReady) {
24+
window.setTimeout(callback);
25+
} else {
26+
window.addEventListener('load', callback, false);
27+
}
28+
}
29+
30+
function createBenchmarkMenu() {
31+
var div = document.createElement('div');
32+
div.innerHTML += '<h1>Benchmarks:</h1><a class="btn btn-default" href="?">All</a>';
33+
for (var i=0; i<benchmarkNames.length; i++) {
34+
var activeClass = useBenchmark(i) ? 'active' : '';
35+
div.innerHTML += ('<a class="btn btn-default '+activeClass+'" href="?'+benchmarkId(i)+'">'+benchmarkNames[i]+'</a>');
36+
}
37+
document.body.insertBefore(div, document.body.childNodes[0]);
38+
}
39+
40+
export function benchmark(name, stepsCreationCallback) {
41+
benchmarkNames.push(name);
42+
if (benchmarkNames.length === 2) {
43+
onLoad(createBenchmarkMenu);
44+
}
45+
if (useBenchmark(benchmarkNames.length-1)) {
46+
stepsCreationCallback();
47+
}
48+
}
49+
50+
export function benchmarkStep(name, callback) {
51+
var benchmarkName = benchmarkNames[benchmarkNames.length-1];
52+
window.benchmarkSteps.push({
53+
name: benchmarkName + '#' + name, fn: callback
54+
});
55+
}

modules/benchmarks/src/compiler/benchmark.dart

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,8 @@ library compiler_benchmark;
22

33
import './selector_benchmark.dart' as sbm;
44
import './compiler_benchmark.dart' as cbm;
5-
import 'dart:js' as js;
65

76
main () {
8-
sbm.setup();
9-
cbm.setup();
10-
11-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
12-
"name": "CssSelector.parse * 1000",
13-
"fn": new js.JsFunction.withThis((_) => sbm.runParse())
14-
}));
15-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
16-
"name": "SelectorMatcher.addSelectable * 1000",
17-
"fn": new js.JsFunction.withThis((_) => sbm.runAdd())
18-
}));
19-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
20-
"name": "SelectorMatcher.match * 1000",
21-
"fn": new js.JsFunction.withThis((_) => sbm.runMatch())
22-
}));
23-
24-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
25-
"name": "Compiler.compile empty template",
26-
"fn": new js.JsFunction.withThis((_) => cbm.compileEmptyTemplate())
27-
}));
28-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
29-
"name": "Compiler.compile 25 element no bindings",
30-
"fn": new js.JsFunction.withThis((_) => cbm.compile25ElementsNoBindings())
31-
}));
32-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
33-
"name": "Compiler.compile 25 element with bindings",
34-
"fn": new js.JsFunction.withThis((_) => cbm.compile25ElementsWithBindings())
35-
}));
7+
sbm.main();
8+
cbm.main();
369
}
Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,9 @@
1-
System.import('benchmarks/compiler/selector_benchmark').then(function (bm) {
2-
bm.setup();
3-
4-
window.benchmarkSteps.push({name: 'CssSelector.parse * 1000', fn: bm.runParse});
5-
}, console.log.bind(console));
6-
7-
System.import('benchmarks/compiler/selector_benchmark').then(function (bm) {
8-
bm.setup();
9-
10-
window.benchmarkSteps.push({name: 'SelectorMatcher.addSelectable * 1000', fn: bm.runAdd});
11-
}, console.log.bind(console));
12-
13-
System.import('benchmarks/compiler/selector_benchmark').then(function (bm) {
14-
bm.setup();
15-
16-
window.benchmarkSteps.push({name: 'SelectorMatcher.match * 1000', fn: bm.runMatch});
17-
}, console.log.bind(console));
18-
19-
System.import('benchmarks/compiler/compiler_benchmark').then(function (bm) {
20-
bm.setup();
21-
22-
window.benchmarkSteps.push({name: 'Compiler.compile empty template', fn: bm.compileEmptyTemplate});
23-
}, console.log.bind(console));
24-
25-
System.import('benchmarks/compiler/compiler_benchmark').then(function (bm) {
26-
bm.setup();
27-
28-
window.benchmarkSteps.push({name: 'Compiler.compile 25 element no bindings', fn: bm.compile25ElementsNoBindings});
29-
}, console.log.bind(console));
30-
31-
System.import('benchmarks/compiler/compiler_benchmark').then(function (bm) {
32-
bm.setup();
33-
34-
window.benchmarkSteps.push({name: 'Compiler.compile 25 element with bindings', fn: bm.compile25ElementsWithBindings});
1+
Promise.all([
2+
System.import('benchmarks/compiler/selector_benchmark'),
3+
System.import('benchmarks/compiler/compiler_benchmark'),
4+
System.import('benchmarks/compiler/compiler_benchmark_ng13')
5+
]).then(function (benchmarks) {
6+
benchmarks.forEach(function(bm) {
7+
bm.main();
8+
});
359
}, console.log.bind(console));

modules/benchmarks/src/compiler/bp.conf.es5

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ module.exports = function(config) {
44
{src: '/js/traceur-runtime.js'},
55
{src: '/js/es6-module-loader-sans-promises.src.js'},
66
{src: '/js/extension-register.js'},
7-
{src: 'register_system.js'},
7+
{src: 'paths.js'},
8+
{src: 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.js'},
89
{src: 'benchmark.js'}
910
]
1011
});
Lines changed: 55 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {DOM} from 'facade/dom';
1+
import {benchmark, benchmarkStep} from '../benchpress';
2+
3+
import {DOM, document} from 'facade/dom';
4+
import {isBlank} from 'facade/lang';
5+
import {MapWrapper} from 'facade/collection';
6+
import {AnnotatedType} from 'core/compiler/annotated_type';
27

38
import {Parser} from 'change_detection/parser/parser';
49
import {ClosureMap} from 'change_detection/parser/closure_map';
@@ -11,39 +16,65 @@ import {Component} from 'core/annotations/component';
1116
import {Decorator} from 'core/annotations/decorator';
1217
import {TemplateConfig} from 'core/annotations/template_config';
1318

19+
var COUNT = 30;
20+
1421
var compiler;
1522
var annotatedComponent;
16-
var annotatedComponentNoDirectives;
17-
18-
var emptyTemplate;
19-
var templateWith25ElementsNoBindings;
20-
var templateWith25ElementsAndBindings;
2123

22-
export function setup() {
24+
function setup() {
2325
var closureMap = new ClosureMap();
24-
var reflector = new Reflector();
26+
var reflector = new CachingReflector();
2527
compiler = new Compiler(null, reflector, new Parser(new Lexer(), closureMap), closureMap);
26-
annotatedComponent = reflector.annotatedType(SomeComponent);
27-
annotatedComponentNoDirectives = reflector.annotatedType(ComponentWithNoDirectives);
28-
29-
emptyTemplate = createTemplate('<div></div>');
30-
templateWith25ElementsNoBindings = buildTemplateWith25ElementsNoBindings();
31-
templateWith25ElementsAndBindings = buildTemplateWith25ElementsAndBindings();
28+
annotatedComponent = reflector.annotatedType(BenchmarkComponent);
3229
}
3330

34-
export function compileEmptyTemplate() {
35-
var template = emptyTemplate;
36-
return compiler.compileWithCache(null, annotatedComponent, template);
31+
export function main() {
32+
setup();
33+
34+
benchmark(`Compiler.compile 5*${COUNT} element no bindings`, function() {
35+
var template = loadTemplate('templateNoBindings', COUNT);
36+
37+
benchmarkStep('run', function() {
38+
// Need to clone every time as the compiler might modify the template!
39+
var cloned = DOM.clone(template);
40+
compiler.compileWithCache(null, annotatedComponent, cloned);
41+
});
42+
});
43+
44+
benchmark(`Compiler.compile 5*${COUNT} element with bindings`, function() {
45+
var template = loadTemplate('templateWithBindings', COUNT);
46+
47+
benchmarkStep('run', function() {
48+
// Need to clone every time as the compiler might modify the template!
49+
var cloned = DOM.clone(template);
50+
compiler.compileWithCache(null, annotatedComponent, cloned);
51+
});
52+
});
3753
}
3854

39-
export function compile25ElementsWithBindings() {
40-
var template = templateWith25ElementsAndBindings;
41-
return compiler.compileWithCache(null, annotatedComponent, template);
55+
function loadTemplate(templateId, repeatCount) {
56+
var template = DOM.querySelectorAll(document, `#${templateId}`)[0];
57+
var content = DOM.getInnerHTML(template);
58+
var result = '';
59+
for (var i=0; i<repeatCount; i++) {
60+
result += content;
61+
}
62+
return DOM.createTemplate(result);
4263
}
4364

44-
export function compile25ElementsNoBindings() {
45-
var template = templateWith25ElementsNoBindings;
46-
return compiler.compileWithCache(null, annotatedComponentNoDirectives, template);
65+
// Caching reflector as reflection in Dart using Mirrors
66+
class CachingReflector extends Reflector {
67+
constructor() {
68+
this._cache = MapWrapper.create();
69+
}
70+
annotatedType(type:Type):AnnotatedType {
71+
var result = MapWrapper.get(this._cache, type);
72+
if (isBlank(result)) {
73+
result = super.annotatedType(type);
74+
MapWrapper.set(this._cache, type, result);
75+
}
76+
return result;
77+
}
4778
}
4879

4980
@Decorator({
@@ -91,79 +122,5 @@ class Dir4 {}
91122
directives: [Dir0, Dir1, Dir2, Dir3, Dir4]
92123
})
93124
})
94-
class SomeComponent {}
125+
class BenchmarkComponent {}
95126

96-
@Component({
97-
template: new TemplateConfig({
98-
directives: []
99-
})
100-
})
101-
class ComponentWithNoDirectives {}
102-
103-
// creates 25 nested divs without bindings, each looking like this:
104-
// <div class="class0 class1 class2 class3 class4 " dir0="" attr0="value0" dir1="" attr1="value1" dir2="" attr2="value2" dir3="" attr3="value3" dir4="" attr4="value4">
105-
// </div>
106-
function buildTemplateWith25ElementsNoBindings() {
107-
var result = '';
108-
for (var i=0; i<5; i++) {
109-
for (var j=0; j<5; j++) {
110-
result += '<div class="';
111-
for (var k=0; k<5; k++) {
112-
result += `class${k} `;
113-
}
114-
result += '"';
115-
for (var k=0; k<5; k++) {
116-
result += ` dir${k}`;
117-
result += ` attr${k}=value${k}`;
118-
}
119-
for (var k=0; k<5; k++) {
120-
result += ` dir${k}`;
121-
result += ` attr${k}=value${k}`;
122-
}
123-
result += '>';
124-
}
125-
for (var j=0; j<5; j++) {
126-
result += '</div>';
127-
}
128-
}
129-
return createTemplate(result);
130-
}
131-
132-
// creates 25 nested divs , each looking like this:
133-
// <div class="class0 class1 class2 class3 class4 " dir0="" [attr0]="value0" dir1="" [attr1]="value1" dir2="" [attr2]="value2" dir3="" [attr3]="value3" dir4="" [attr4]="value4">
134-
// {{inter0}}{{inter1}}{{inter2}}{{inter3}}{{inter4}}
135-
// </div>
136-
function buildTemplateWith25ElementsAndBindings() {
137-
var result = '';
138-
for (var i=0; i<5; i++) {
139-
for (var j=0; j<5; j++) {
140-
result += '<div class="';
141-
for (var k=0; k<5; k++) {
142-
result += `class${k} `;
143-
}
144-
result += '"';
145-
for (var k=0; k<5; k++) {
146-
result += ` dir${k}`;
147-
result += ` [attr${k}]=value${k}`;
148-
}
149-
for (var k=0; k<5; k++) {
150-
result += ` dir${k}`;
151-
result += ` [attr${k}]=value${k}`;
152-
}
153-
result += '>';
154-
for (var k=0; k<5; k++) {
155-
result += `{{inter${k}}}`;
156-
}
157-
}
158-
for (var j=0; j<5; j++) {
159-
result += '</div>';
160-
}
161-
}
162-
return createTemplate(result);
163-
}
164-
165-
166-
167-
function createTemplate(html) {
168-
return DOM.createTemplate(html);
169-
}

0 commit comments

Comments
 (0)