benchmark(change_detection): added a new set of benchmarks measuring updating properties

This commit is contained in:
vsavkin 2015-04-06 07:52:49 -07:00
parent 2560af731a
commit 9c62b5867e
3 changed files with 232 additions and 88 deletions

View File

@ -6,11 +6,11 @@ describe('ng2 change detection benchmark', function () {
afterEach(perfUtil.verifyNoBrowserErrors);
it('should log ng stats (dynamic)', function(done) {
it('should log ng stats (dynamic, reads)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#ng2ChangeDetectionDynamic'],
id: 'ng2.changeDetection.dynamic',
buttons: ['#ng2ChangeDetectionDynamicReads'],
id: 'ng2.changeDetection.dynamic.reads',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}
@ -21,11 +21,11 @@ describe('ng2 change detection benchmark', function () {
}).then(done, done.fail);
});
it('should log ng stats (jit)', function(done) {
it('should log ng stats (dynamic, writes)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#ng2ChangeDetectionJit'],
id: 'ng2.changeDetection.jit',
buttons: ['#ng2ChangeDetectionDynamicWrites'],
id: 'ng2.changeDetection.dynamic.writes',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}
@ -36,11 +36,56 @@ describe('ng2 change detection benchmark', function () {
}).then(done, done.fail);
});
it('should log baseline stats', function(done) {
it('should log ng stats (jit, reads)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#baselineChangeDetection'],
id: 'baseline.changeDetection',
buttons: ['#ng2ChangeDetectionJitReads'],
id: 'ng2.changeDetection.jit.reads',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}
],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail);
});
it('should log ng stats (jit, writes)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#ng2ChangeDetectionJitWrites'],
id: 'ng2.changeDetection.jit.writes',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}
],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail);
});
it('should log baseline stats (create)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#baselineChangeDetectionReads'],
id: 'baseline.changeDetection.reads',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}
],
microMetrics: {
'detectChangesAvg': 'avg time to detect changes (ms)'
}
}).then(done, done.fail);
});
it('should log baseline stats (update)', function(done) {
perfUtil.runClickBenchmark({
url: URL,
buttons: ['#baselineChangeDetectionWrites'],
id: 'baseline.changeDetection.writes',
params: [
{name: 'numberOfChecks', value: 900000},
{name: 'iterations', value: 20, scale: 'linear'}

View File

@ -14,9 +14,14 @@
<h2>Actions</h2>
<p>
<button id="ng2ChangeDetectionDynamic">Ng2 detect changes (dynamic)</button>
<button id="ng2ChangeDetectionJit">Ng2 detect changes (jit)</button>
<button id="baselineChangeDetection">baselineDetectChanges</button>
<button id="ng2ChangeDetectionDynamicReads">Ng2 detect changes (reads, dynamic)</button>
<button id="ng2ChangeDetectionDynamicWrites">Ng2 detect changes (writes, dynamic)</button>
<button id="ng2ChangeDetectionJitReads">Ng2 detect changes (reads, jit)</button>
<button id="ng2ChangeDetectionJitWrites">Ng2 detect changes (writes, jit)</button>
<button id="baselineChangeDetectionReads">baselineDetectChangesReads</button>
<button id="baselineChangeDetectionWrites">baselineDetectChangesWrites</button>
</p>
$SCRIPTS$

View File

@ -15,6 +15,8 @@ import {
} from 'angular2/change_detection';
// ---- SHARED
class Obj {
field0;
field1;
@ -42,14 +44,54 @@ class Obj {
case 9: this.field9 = value; break;
}
}
getField(index) {
switch (index) {
case 0: return this.field0;
case 1: return this.field1;
case 2: return this.field2;
case 3: return this.field3;
case 4: return this.field4;
case 5: return this.field5;
case 6: return this.field6;
case 7: return this.field7;
case 8: return this.field8;
case 9: return this.field9;
}
}
}
class Row {
currentValue;
previousValue;
obj;
targetObj;
field0;
field1;
field2;
field3;
field4;
field5;
field6;
field7;
field8;
field9;
next;
}
function createObject() {
var obj = new Obj();
for (var i = 0; i < 10; ++i) {
obj.setField(i, i);
}
return obj;
}
function changeObject(object) {
for (var i = 0; i < 10; ++i) {
object.setField(i, object.getField(i) + 1);
}
}
function setUpReflector() {
reflector.registerGetters({
'field0': function(obj){return obj.field0},
@ -77,16 +119,15 @@ function setUpReflector() {
});
}
function setUpBaseline(iterations) {
function createRow(i) {
var obj = new Obj();
for (var j = 0; j < 10; ++j) {
obj.setField(j, i);
}
// ---- BASELINE
function setUpBaseline(iterations, object) {
function createRow(i) {
var r = new Row();
r.currentValue = obj;
r.previousValue = obj;
r.obj = object;
r.targetObj = new Obj();
return r;
}
@ -100,39 +141,92 @@ function setUpBaseline(iterations) {
return head;
}
function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
function checkBaselineRow(r) {
var obj = r.obj;
if (obj.field0 !== r.field0) {r.field0 = obj.field0; r.targetObj.field0 = obj.field0; }
if (obj.field1 !== r.field1) {r.field1 = obj.field1; r.targetObj.field1 = obj.field1; }
if (obj.field2 !== r.field2) {r.field2 = obj.field2; r.targetObj.field2 = obj.field2; }
if (obj.field3 !== r.field3) {r.field3 = obj.field3; r.targetObj.field3 = obj.field3; }
if (obj.field4 !== r.field4) {r.field4 = obj.field4; r.targetObj.field4 = obj.field4; }
if (obj.field5 !== r.field5) {r.field5 = obj.field5; r.targetObj.field5 = obj.field5; }
if (obj.field6 !== r.field6) {r.field6 = obj.field6; r.targetObj.field6 = obj.field6; }
if (obj.field7 !== r.field7) {r.field7 = obj.field7; r.targetObj.field7 = obj.field7; }
if (obj.field8 !== r.field8) {r.field8 = obj.field8; r.targetObj.field8 = obj.field8; }
if (obj.field9 !== r.field9) {r.field9 = obj.field9; r.targetObj.field9 = obj.field9; }
}
function runBaselineChangeDetection(baselineHead){
var current = baselineHead;
while (isPresent(current)) {
checkBaselineRow(current);
current = current.next;
}
}
function runBaselineReads(baselineHead, numberOfRuns) {
for (var i = 0; i < numberOfRuns; ++i) {
runBaselineChangeDetection(baselineHead);
}
}
function runBaselineWrites(baselineHead, numberOfRuns, object) {
for (var i = 0; i < numberOfRuns; ++i) {
changeObject(object);
runBaselineChangeDetection(baselineHead);
}
}
// ---- CHANGE DETECTION
function setUpChangeDetection(changeDetection:ChangeDetection, iterations, object) {
var dispatcher = new DummyDispatcher();
var parser = new Parser(new Lexer());
var parentProto = changeDetection.createProtoChangeDetector('parent');
var parentCd = parentProto.instantiate(dispatcher, [], [], []);
var targetObj = new Obj();
var proto = changeDetection.createProtoChangeDetector("proto");
var bindingRecords = [
new BindingRecord(parser.parseBinding('field0', null), "memo", null),
new BindingRecord(parser.parseBinding('field1', null), "memo", null),
new BindingRecord(parser.parseBinding('field2', null), "memo", null),
new BindingRecord(parser.parseBinding('field3', null), "memo", null),
new BindingRecord(parser.parseBinding('field4', null), "memo", null),
new BindingRecord(parser.parseBinding('field5', null), "memo", null),
new BindingRecord(parser.parseBinding('field6', null), "memo", null),
new BindingRecord(parser.parseBinding('field7', null), "memo", null),
new BindingRecord(parser.parseBinding('field8', null), "memo", null),
new BindingRecord(parser.parseBinding('field9', null), "memo", null)
new BindingRecord(parser.parseBinding('field0', null), new FakeBindingMemento(targetObj, reflector.setter("field0")), null),
new BindingRecord(parser.parseBinding('field1', null), new FakeBindingMemento(targetObj, reflector.setter("field1")), null),
new BindingRecord(parser.parseBinding('field2', null), new FakeBindingMemento(targetObj, reflector.setter("field2")), null),
new BindingRecord(parser.parseBinding('field3', null), new FakeBindingMemento(targetObj, reflector.setter("field3")), null),
new BindingRecord(parser.parseBinding('field4', null), new FakeBindingMemento(targetObj, reflector.setter("field4")), null),
new BindingRecord(parser.parseBinding('field5', null), new FakeBindingMemento(targetObj, reflector.setter("field5")), null),
new BindingRecord(parser.parseBinding('field6', null), new FakeBindingMemento(targetObj, reflector.setter("field6")), null),
new BindingRecord(parser.parseBinding('field7', null), new FakeBindingMemento(targetObj, reflector.setter("field7")), null),
new BindingRecord(parser.parseBinding('field8', null), new FakeBindingMemento(targetObj, reflector.setter("field8")), null),
new BindingRecord(parser.parseBinding('field9', null), new FakeBindingMemento(targetObj, reflector.setter("field9")), null)
];
for (var i = 0; i < iterations; ++i) {
var obj = new Obj();
for (var j = 0; j < 10; ++j) {
obj.setField(j, i);
}
var cd = proto.instantiate(dispatcher, bindingRecords, [], []);
cd.hydrate(obj, null);
cd.hydrate(object, null);
parentCd.addChild(cd);
}
return parentCd;
}
function runChangeDetectionReads(changeDetector, numberOfRuns) {
for(var i = 0; i < numberOfRuns; ++i) {
changeDetector.detectChanges();
}
}
function runChangeDetectionWrites(changeDetector, numberOfRuns, object) {
for(var i = 0; i < numberOfRuns; ++i) {
changeObject(object);
changeDetector.detectChanges();
}
}
export function main () {
BrowserDomAdapter.makeCurrent();
var numberOfChecks = getIntParameter('numberOfChecks');
@ -142,79 +236,79 @@ export function main () {
var numberOfDetectors = numberOfChecks / numberOfChecksPerDetector / numberOfRuns;
setUpReflector();
var object = createObject()
// -- BASELINE
function checkBaselineRow(r) {
var curr = r.currentValue;
var prev = r.previousValue;
if (curr.field0 !== prev.field0) throw "should not happen";
if (curr.field1 !== prev.field1) throw "should not happen";
if (curr.field2 !== prev.field2) throw "should not happen";
if (curr.field3 !== prev.field3) throw "should not happen";
if (curr.field4 !== prev.field4) throw "should not happen";
if (curr.field5 !== prev.field5) throw "should not happen";
if (curr.field6 !== prev.field6) throw "should not happen";
if (curr.field7 !== prev.field7) throw "should not happen";
if (curr.field8 !== prev.field8) throw "should not happen";
if (curr.field9 !== prev.field9) throw "should not happen";
}
var baselineHead = setUpBaseline(numberOfDetectors);
function runBaselineChangeDetection(){
var current = baselineHead;
while (isPresent(current)) {
checkBaselineRow(current);
current = current.next;
}
}
function baselineChangeDetection() {
for (var i = 0; i < numberOfRuns; ++i) {
runBaselineChangeDetection();
}
}
runBaselineChangeDetection();
var baselineHead = setUpBaseline(numberOfDetectors, object);
runBaselineReads(baselineHead, 1); //warmup
bindAction(
'#baselineChangeDetection',
() => microBenchmark('detectChangesAvg', numberOfRuns, baselineChangeDetection)
'#baselineChangeDetectionReads',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runBaselineReads(baselineHead, numberOfRuns))
);
bindAction(
'#baselineChangeDetectionWrites',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runBaselineWrites(baselineHead, numberOfRuns, object))
);
// -- DYNAMIC
var ng2DynamicChangeDetector = setUpChangeDetection(dynamicChangeDetection, numberOfDetectors);
function ng2ChangeDetectionDynamic() {
for(var i = 0; i < numberOfRuns; ++i) {
ng2DynamicChangeDetector.detectChanges();
}
}
ng2DynamicChangeDetector.detectChanges();
var ng2DynamicChangeDetector = setUpChangeDetection(dynamicChangeDetection, numberOfDetectors, object);
runChangeDetectionReads(ng2DynamicChangeDetector, 1); //warmup
bindAction(
'#ng2ChangeDetectionDynamic',
() => microBenchmark('detectChangesAvg', numberOfRuns, ng2ChangeDetectionDynamic)
'#ng2ChangeDetectionDynamicReads',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runChangeDetectionReads(ng2DynamicChangeDetector, numberOfRuns))
);
bindAction(
'#ng2ChangeDetectionDynamicWrites',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runChangeDetectionWrites(ng2DynamicChangeDetector, numberOfRuns, object))
);
// -- JIT
// Reenable when we have transformers for Dart
if (isJsObject({})) {
var ng2JitChangeDetector = setUpChangeDetection(jitChangeDetection, numberOfDetectors);
var ng2JitChangeDetector = setUpChangeDetection(jitChangeDetection, numberOfDetectors, object);
function ng2ChangeDetectionJit() {
for (var i = 0; i < numberOfRuns; ++i) {
ng2JitChangeDetector.detectChanges();
}
}
runChangeDetectionReads(ng2JitChangeDetector, 1); //warmup
ng2JitChangeDetector.detectChanges();
bindAction(
'#ng2ChangeDetectionJit',
() => microBenchmark('detectChangesAvg', numberOfRuns, ng2ChangeDetectionJit)
'#ng2ChangeDetectionJitReads',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runChangeDetectionReads(ng2JitChangeDetector, numberOfRuns))
);
bindAction(
'#ng2ChangeDetectionJitWrites',
() => microBenchmark('detectChangesAvg', numberOfRuns, () => runChangeDetectionWrites(ng2JitChangeDetector, numberOfRuns, object))
);
} else {
bindAction('#ng2ChangeDetectionJit', () => {});
bindAction('#ng2ChangeDetectionJitReads', () => {});
bindAction('#ng2ChangeDetectionJitWrites', () => {});
}
}
class FakeBindingMemento {
setter:Function;
targetObj:Obj;
constructor(targetObj:Obj, setter:Function) {
this.targetObj = targetObj;
this.setter = setter;
}
}
class DummyDispatcher extends ChangeDispatcher {
invokeMementoFor(binding, newValue) {
invokeMementoFor(bindingMemento, newValue) {
var obj = bindingMemento.targetObj;
bindingMemento.setter(obj, newValue);
}
}