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;
|
import 'dart:js' as js;
|
||||||
|
|
||||||
main () {
|
main () {
|
||||||
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
|
cdb.run();
|
||||||
"name": "Change Detection",
|
|
||||||
"fn": new js.JsFunction.withThis((_) => cdb.run())
|
|
||||||
}));
|
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
System.import('benchmarks/change_detection/change_detection_benchmark').then(function (bm) {
|
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));
|
}, console.log.bind(console));
|
|
@ -2,6 +2,8 @@ import {ListWrapper, MapWrapper} from 'facade/collection';
|
||||||
import {Parser} from 'change_detection/parser/parser';
|
import {Parser} from 'change_detection/parser/parser';
|
||||||
import {Lexer} from 'change_detection/parser/lexer';
|
import {Lexer} from 'change_detection/parser/lexer';
|
||||||
import {reflector} from 'reflection/reflection';
|
import {reflector} from 'reflection/reflection';
|
||||||
|
import {isPresent} from 'facade/lang';
|
||||||
|
import {benchmark, benchmarkStep} from '../benchpress';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetector,
|
ChangeDetector,
|
||||||
|
@ -10,51 +12,175 @@ import {
|
||||||
} from 'change_detection/change_detector';
|
} 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 () {
|
export function run () {
|
||||||
reflector.registerGetters({
|
setUpReflector();
|
||||||
'a': function(obj){return obj.a},
|
|
||||||
'b': function(obj){return obj.b},
|
benchmark(`Baseline`, function () {
|
||||||
'c': function(obj){return obj.c}
|
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({
|
benchmark(`Change Detection`, function() {
|
||||||
'a': function(obj, v){return obj.a = v},
|
var cd = setUpChangeDetection();
|
||||||
'b': function(obj, v){return obj.b = v},
|
|
||||||
'c': function(obj, v){return obj.c = v}
|
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 {
|
class DummyDispatcher extends WatchGroupDispatcher {
|
||||||
onRecordChange(record, context) {
|
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 currentRange = record.recordRange;
|
||||||
var currentGroup = record.groupMemento();
|
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()) {
|
if (record.check()) {
|
||||||
count ++;
|
count ++;
|
||||||
if (record.terminatesExpression()) {
|
if (record.terminatesExpression()) {
|
||||||
|
@ -33,9 +29,15 @@ export class ChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._shouldNotifyDispatcher(currentRange, nextRange, currentGroup, nextGroup, updatedRecords)) {
|
if (isPresent(updatedRecords)) {
|
||||||
currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords);
|
var nextEnabled = record.nextEnabled;
|
||||||
updatedRecords = null;
|
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;
|
record = record.nextEnabled;
|
||||||
|
@ -44,14 +46,6 @@ export class ChangeDetector {
|
||||||
return count;
|
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) {
|
_addRecord(updatedRecords:List, record:Record) {
|
||||||
if (isBlank(updatedRecords)) {
|
if (isBlank(updatedRecords)) {
|
||||||
updatedRecords = _singleElementList;
|
updatedRecords = _singleElementList;
|
||||||
|
|
Loading…
Reference in New Issue