refactor(view_compiler): codegen DI and Queries

BREAKING CHANGE:
- Renderer:
  * renderComponent method is removed form `Renderer`, only present on `RootRenderer`
  * Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor
    now take the DebugInfo directly.
- Query semantics:
  * Queries don't work with dynamically loaded components.
  * e.g. for router-outlet: loaded components can't be queries via @ViewQuery,
    but router-outlet emits an event `activate` now that emits the activated component
- Exception classes and the context inside changed (renamed fields)
- DebugElement.attributes is an Object and not a Map in JS any more
- ChangeDetectorGenConfig was renamed into CompilerConfig
- AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer
  are removed, use the methods in ViewContainerRef instead
- Change detection order changed:
  * 1. dirty check component inputs
  * 2. dirty check content children
  * 3. update render nodes

Closes #6301
Closes #6567
This commit is contained in:
Tobias Bosch
2016-01-06 14:13:44 -08:00
parent 45f09ba686
commit 2b34c88b69
312 changed files with 14271 additions and 16566 deletions

View File

@ -1,29 +0,0 @@
<!doctype html>
<html>
<body>
<h2>Params</h2>
<form>
Iterations:
<input type="number" name="iterations" placeholder="iterations" value="20">
Number of checks:
<input type="number" name="numberOfChecks" placeholder="numberOfChecks" value="900000">
<br>
<button>Apply</button>
</form>
<h2>Actions</h2>
<p>
<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$
</body>

View File

@ -1,389 +0,0 @@
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 parser = new Parser(new Lexer());
var genConfig = new ChangeDetectorGenConfig(false, false, true);
var parentProto = protoChangeDetectorFactory(
new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
var parentCd = parentProto.instantiate();
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 DummyDispatcher(targetObj), null);
for (var i = 0; i < iterations; ++i) {
var cd = proto.instantiate();
cd.hydrate(object, null, new DummyDispatcher(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 DummyDispatcher implements ChangeDispatcher {
targetObj: Obj;
constructor(targetObj) { this.targetObj = targetObj; }
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext {
throw "getDebugContext not implemented.";
}
notifyOnBinding(bindingTarget, newValue) { throw "Should not be used"; }
logBindingUpdate(bindingTarget, newValue) { throw "Should not be used"; }
notifyAfterContentChecked() {}
notifyAfterViewChecked() {}
notifyOnDestroy() {}
getDetectorFor(directiveIndex: DirectiveIndex): any { throw "getDetectorFor not implemented."; }
getDirectiveFor(record) { return this.targetObj; }
}

View File

@ -16,8 +16,7 @@ import {
ViewMetadata
} from 'angular2/core';
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
import {CompilerConfig, ViewResolver} from 'angular2/compiler';
import {getIntParameter, bindAction} from 'angular2/src/testing/benchmark_util';
@ -31,9 +30,9 @@ function _createBindings(): Provider[] {
[BenchmarkComponentNoBindings, BenchmarkComponentWithBindings]),
deps: []
}),
// Use DynamicChangeDetector as that is the only one that Dart supports as well
// so that we can compare the numbers between JS and Dart
provide(ChangeDetectorGenConfig, {useValue: new ChangeDetectorGenConfig(false, false, false)})
// Use interpretative mode as Dart does not support JIT and
// we want to be able to compare the numbers between JS and Dart
provide(CompilerConfig, {useValue: new CompilerConfig(false, false, false)})
];
}
@ -106,25 +105,30 @@ class CompilerAppComponent {
@Directive({selector: '[dir0]', inputs: ['prop: attr0']})
class Dir0 {
prop: any;
}
@Directive({selector: '[dir1]', inputs: ['prop: attr1']})
class Dir1 {
prop: any;
constructor(dir0: Dir0) {}
}
@Directive({selector: '[dir2]', inputs: ['prop: attr2']})
class Dir2 {
prop: any;
constructor(dir1: Dir1) {}
}
@Directive({selector: '[dir3]', inputs: ['prop: attr3']})
class Dir3 {
prop: any;
constructor(dir2: Dir2) {}
}
@Directive({selector: '[dir4]', inputs: ['prop: attr4']})
class Dir4 {
prop: any;
constructor(dir3: Dir3) {}
}
@ -168,4 +172,15 @@ class BenchmarkComponentNoBindings {
</div>`
})
class BenchmarkComponentWithBindings {
value0: any;
value1: any;
value2: any;
value3: any;
value4: any;
inter0: any;
inter1: any;
inter2: any;
inter3: any;
inter4: any;
}

View File

@ -5,9 +5,6 @@
<li>
<a href="di/di_benchmark.html">DI benchmark</a>
</li>
<li>
<a href="change_detection/change_detection_benchmark.html">Change detection benchmark</a>
</li>
<li>
<a href="compiler/selector_benchmark.html">Selector benchmark</a>
</li>

View File

@ -6,7 +6,8 @@ import {
ViewContainerRef,
bind,
provide,
Provider
Provider,
enableProdMode
} from 'angular2/core';
import {NgIf} from 'angular2/common';
@ -32,6 +33,7 @@ var BASELINE_IF_TEMPLATE;
export function main() {
BrowserDomAdapter.makeCurrent();
enableProdMode();
var maxDepth = getIntParameter('depth');
BASELINE_TREE_TEMPLATE = DOM.createTemplate(