Currently, core depends on DomRenderer, which depends on the browser. This means that if you depend on angular2/core, you will always pull in the browser dom adapter and the browser render, regardless if you need them or not. This PR moves the browser dom adapter and the browser renderer out of core. BREAKING CHANGE If you import browser adapter or dom renderer directly (not via angular2/core), you will have to change the import path.
394 lines
11 KiB
TypeScript
394 lines
11 KiB
TypeScript
import {reflector} from 'angular2/src/core/reflection/reflection';
|
|
import {isPresent} from 'angular2/src/facade/lang';
|
|
import {getIntParameter, bindAction, microBenchmark} from 'angular2/src/testing/benchmark_util';
|
|
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
|
|
|
|
import {
|
|
Lexer,
|
|
Parser,
|
|
ChangeDispatcher,
|
|
DebugContext,
|
|
DynamicProtoChangeDetector,
|
|
JitProtoChangeDetector,
|
|
ChangeDetectorDefinition,
|
|
ChangeDetectorGenConfig,
|
|
BindingRecord,
|
|
DirectiveRecord,
|
|
DirectiveIndex
|
|
} from 'angular2/src/core/change_detection/change_detection';
|
|
|
|
|
|
// ---- SHARED
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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 },
|
|
'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 }
|
|
});
|
|
}
|
|
|
|
|
|
|
|
// ---- BASELINE
|
|
|
|
function setUpBaseline(iterations, object) {
|
|
function createRow(i) {
|
|
var r = new Row();
|
|
r.obj = object;
|
|
r.targetObj = new Obj();
|
|
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 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(protoChangeDetectorFactory: Function, iterations, object) {
|
|
var dispatcher = new DummyDispatcher();
|
|
var parser = new Parser(new Lexer());
|
|
|
|
var genConfig = new ChangeDetectorGenConfig(false, false, true);
|
|
var parentProto = protoChangeDetectorFactory(
|
|
new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
|
|
var parentCd = parentProto.instantiate(dispatcher);
|
|
|
|
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
|
|
var bindings = [
|
|
BindingRecord.createForDirective(parser.parseBinding('field0', null), "field0",
|
|
reflector.setter("field0"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field1', null), "field1",
|
|
reflector.setter("field1"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field2', null), "field2",
|
|
reflector.setter("field2"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field3', null), "field3",
|
|
reflector.setter("field3"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field4', null), "field4",
|
|
reflector.setter("field4"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field5', null), "field5",
|
|
reflector.setter("field5"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field6', null), "field6",
|
|
reflector.setter("field6"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field7', null), "field7",
|
|
reflector.setter("field7"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field8', null), "field8",
|
|
reflector.setter("field8"), directiveRecord),
|
|
BindingRecord.createForDirective(parser.parseBinding('field9', null), "field9",
|
|
reflector.setter("field9"), directiveRecord)
|
|
];
|
|
|
|
var proto = protoChangeDetectorFactory(
|
|
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], genConfig));
|
|
|
|
var targetObj = new Obj();
|
|
parentCd.hydrate(object, null, new FakeDirectives(targetObj), null);
|
|
for (var i = 0; i < iterations; ++i) {
|
|
var cd = proto.instantiate(dispatcher);
|
|
cd.hydrate(object, null, new FakeDirectives(targetObj), null);
|
|
parentCd.addContentChild(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');
|
|
var numberOfRuns = getIntParameter('iterations');
|
|
|
|
var numberOfChecksPerDetector = 10;
|
|
var numberOfDetectors = numberOfChecks / numberOfChecksPerDetector / numberOfRuns;
|
|
|
|
setUpReflector();
|
|
var object = createObject()
|
|
|
|
// -- BASELINE
|
|
var baselineHead = setUpBaseline(numberOfDetectors, object);
|
|
|
|
runBaselineReads(baselineHead, 1); // warmup
|
|
|
|
bindAction('#baselineChangeDetectionReads',
|
|
() => microBenchmark('detectChangesAvg', numberOfRuns,
|
|
() => runBaselineReads(baselineHead, numberOfRuns)));
|
|
|
|
bindAction('#baselineChangeDetectionWrites',
|
|
() => microBenchmark('detectChangesAvg', numberOfRuns,
|
|
() => runBaselineWrites(baselineHead, numberOfRuns, object)));
|
|
|
|
|
|
|
|
// -- DYNAMIC
|
|
var ng2DynamicChangeDetector = setUpChangeDetection(
|
|
(changeDetectorDefinition) => new DynamicProtoChangeDetector(changeDetectorDefinition),
|
|
numberOfDetectors, object);
|
|
|
|
runChangeDetectionReads(ng2DynamicChangeDetector, 1); // warmup
|
|
|
|
bindAction(
|
|
'#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 (JitProtoChangeDetector.isSupported()) {
|
|
var ng2JitChangeDetector = setUpChangeDetection(
|
|
(changeDetectorDefinition) => new JitProtoChangeDetector(changeDetectorDefinition),
|
|
numberOfDetectors, object);
|
|
|
|
runChangeDetectionReads(ng2JitChangeDetector, 1); // warmup
|
|
|
|
bindAction(
|
|
'#ng2ChangeDetectionJitReads',
|
|
() => microBenchmark('detectChangesAvg', numberOfRuns,
|
|
() => runChangeDetectionReads(ng2JitChangeDetector, numberOfRuns)));
|
|
|
|
bindAction('#ng2ChangeDetectionJitWrites',
|
|
() => microBenchmark(
|
|
'detectChangesAvg', numberOfRuns,
|
|
() => runChangeDetectionWrites(ng2JitChangeDetector, numberOfRuns, object)));
|
|
} else {
|
|
bindAction('#ng2ChangeDetectionJitReads', () => {});
|
|
bindAction('#ng2ChangeDetectionJitWrites', () => {});
|
|
}
|
|
}
|
|
|
|
class FakeDirectives {
|
|
targetObj: Obj;
|
|
|
|
constructor(targetObj) { this.targetObj = targetObj; }
|
|
|
|
getDirectiveFor(record) { return this.targetObj; }
|
|
}
|
|
|
|
class DummyDispatcher implements ChangeDispatcher {
|
|
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
|
|
throw "getDebugContext not implemented.";
|
|
}
|
|
notifyOnBinding(bindingTarget, newValue) { throw "Should not be used"; }
|
|
logBindingUpdate(bindingTarget, newValue) { throw "Should not be used"; }
|
|
notifyAfterContentChecked() {}
|
|
notifyAfterViewChecked() {}
|
|
}
|