perf(change_detection): add baseline to change detection benchmark
This commit is contained in:
parent
847cefcb7b
commit
65242fbd6d
|
@ -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();
|
||||
}
|
|
@ -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));
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue