perf(change_detection): add baseline to change detection benchmark

This commit is contained in:
vsavkin 2014-12-03 11:26:39 -08:00
parent 847cefcb7b
commit 65242fbd6d
4 changed files with 173 additions and 56 deletions

View File

@ -4,8 +4,5 @@ import './change_detection_benchmark.dart' as cdb;
import 'dart:js' as js;
main () {
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
"name": "Change Detection",
"fn": new js.JsFunction.withThis((_) => cdb.run())
}));
cdb.run();
}

View File

@ -1,3 +1,3 @@
System.import('benchmarks/change_detection/change_detection_benchmark').then(function (bm) {
window.benchmarkSteps.push({name: 'ChangeDetection', fn: bm.run});
bm.run();
}, console.log.bind(console));

View File

@ -2,6 +2,8 @@ import {ListWrapper, MapWrapper} from 'facade/collection';
import {Parser} from 'change_detection/parser/parser';
import {Lexer} from 'change_detection/parser/lexer';
import {reflector} from 'reflection/reflection';
import {isPresent} from 'facade/lang';
import {benchmark, benchmarkStep} from '../benchpress';
import {
ChangeDetector,
@ -10,51 +12,175 @@ import {
} from 'change_detection/change_detector';
var ITERATIONS = 100000;
var ITERATIONS = 200000;
class Obj {
field0;
field1;
field2;
field3;
field4;
field5;
field6;
field7;
field8;
field9;
setField(index, value) {
switch (index) {
case 0: this.field0 = value; break;
case 1: this.field1 = value; break;
case 2: this.field2 = value; break;
case 3: this.field3 = value; break;
case 4: this.field4 = value; break;
case 5: this.field5 = value; break;
case 6: this.field6 = value; break;
case 7: this.field7 = value; break;
case 8: this.field8 = value; break;
case 9: this.field9 = value; break;
}
}
}
class Row {
previousValue;
obj;
getter;
next;
}
function setUpReflector() {
reflector.registerGetters({
'field0': function(obj){return obj.field0},
'field1': function(obj){return obj.field1},
'field2': function(obj){return obj.field2},
'field3': function(obj){return obj.field3},
'field4': function(obj){return obj.field4},
'field5': function(obj){return obj.field5},
'field6': function(obj){return obj.field6},
'field7': function(obj){return obj.field7},
'field8': function(obj){return obj.field8},
'field9': function(obj){return obj.field9}
});
reflector.registerSetters({
'field0': function(obj, v){return obj.field0 = v},
'field1': function(obj, v){return obj.field1 = v},
'field2': function(obj, v){return obj.field2 = v},
'field3': function(obj, v){return obj.field3 = v},
'field4': function(obj, v){return obj.field4 = v},
'field5': function(obj, v){return obj.field5 = v},
'field6': function(obj, v){return obj.field6 = v},
'field7': function(obj, v){return obj.field7 = v},
'field8': function(obj, v){return obj.field8 = v},
'field9': function(obj, v){return obj.field9 = v}
});
}
function setUpBaseline() {
function createRow(i) {
var obj = new Obj();
var index = i % 10;
obj.setField(index, i);
var r = new Row();
r.obj = obj;
r.previousValue = i;
r.getter = reflector.getter(`field${index}`);
return r;
}
var head = createRow(0);
var current = head;
for (var i = 1; i < ITERATIONS; i++) {
var newRow = createRow(i);
current.next = newRow;
current = newRow;
}
return head;
}
function setUpChangeDetection() {
var dispatcher = new DummyDispatcher();
var parser = new Parser(new Lexer());
var parentProto = new ProtoRecordRange();
var parentRange = parentProto.instantiate(dispatcher, MapWrapper.create());
var astWithSource = [
parser.parseBinding('field0'),
parser.parseBinding('field1'),
parser.parseBinding('field2'),
parser.parseBinding('field3'),
parser.parseBinding('field4'),
parser.parseBinding('field5'),
parser.parseBinding('field6'),
parser.parseBinding('field7'),
parser.parseBinding('field8'),
parser.parseBinding('field9')
];
function proto(i) {
var prr = new ProtoRecordRange();
prr.addRecordsFromAST(astWithSource[i % 10].ast, "memo", i, false);
return prr;
}
var prr = [
proto(0),
proto(1),
proto(2),
proto(3),
proto(4),
proto(5),
proto(6),
proto(7),
proto(8),
proto(9)
];
for (var i = 0; i < ITERATIONS; ++i) {
var obj = new Obj();
var index = i % 10;
obj.setField(index, i);
var rr = prr[index].instantiate(dispatcher, null);
rr.setContext(obj);
parentRange.addRange(rr);
}
return new ChangeDetector(parentRange);
}
export function run () {
reflector.registerGetters({
'a': function(obj){return obj.a},
'b': function(obj){return obj.b},
'c': function(obj){return obj.c}
setUpReflector();
benchmark(`Baseline`, function () {
var head = setUpBaseline();
benchmarkStep('run', function () {
var current = head;
while (isPresent(current)) {
if (current.getter(current.obj) !== current.previousValue) {
throw "should not happen";
}
current = current.next;
}
});
});
reflector.registerSetters({
'a': function(obj, v){return obj.a = v},
'b': function(obj, v){return obj.b = v},
'c': function(obj, v){return obj.c = v}
benchmark(`Change Detection`, function() {
var cd = setUpChangeDetection();
benchmarkStep('run', function() {
cd.detectChanges();
});
});
var parser = new Parser(new Lexer());
var astWithSource = parser.parseBinding('a + b * c');
var prr = new ProtoRecordRange();
prr.addRecordsFromAST(astWithSource.ast, 'memo', false);
var dispatcher = new DummyDispatcher();
var rr = prr.instantiate(dispatcher, MapWrapper.create());
rr.setContext(new Component());
var cd = new ChangeDetector(rr);
for (var i = 0; i < ITERATIONS; ++i) {
cd.detectChanges();
}
}
class DummyDispatcher extends WatchGroupDispatcher {
onRecordChange(record, context) {
}
}
class Component {
a:number;
b:number;
c:number;
constructor() {
this.a = 1;
this.b = 2;
this.c = 3;
}
}

View File

@ -22,10 +22,6 @@ export class ChangeDetector {
var currentRange = record.recordRange;
var currentGroup = record.groupMemento();
var nextEnabled = record.nextEnabled;
var nextRange = isPresent(nextEnabled) ? nextEnabled.recordRange : null;
var nextGroup = isPresent(nextEnabled) ? nextEnabled.groupMemento() : null;
if (record.check()) {
count ++;
if (record.terminatesExpression()) {
@ -33,9 +29,15 @@ export class ChangeDetector {
}
}
if (this._shouldNotifyDispatcher(currentRange, nextRange, currentGroup, nextGroup, updatedRecords)) {
currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords);
updatedRecords = null;
if (isPresent(updatedRecords)) {
var nextEnabled = record.nextEnabled;
var nextRange = isPresent(nextEnabled) ? nextEnabled.recordRange : null;
var nextGroup = isPresent(nextEnabled) ? nextEnabled.groupMemento() : null;
if (currentRange != nextRange || currentGroup != nextGroup) {
currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords);
updatedRecords = null;
}
}
record = record.nextEnabled;
@ -44,14 +46,6 @@ export class ChangeDetector {
return count;
}
_groupChanged(currentRange, nextRange, currentGroup, nextGroup) {
return currentRange != nextRange || currentGroup != nextGroup;
}
_shouldNotifyDispatcher(currentRange, nextRange, currentGroup, nextGroup, updatedRecords) {
return this._groupChanged(currentRange, nextRange, currentGroup, nextGroup) && isPresent(updatedRecords);
}
_addRecord(updatedRecords:List, record:Record) {
if (isBlank(updatedRecords)) {
updatedRecords = _singleElementList;