perf(change_detection): use object pools not to create unnecessary garbage

This commit is contained in:
vsavkin 2015-01-27 17:30:32 -08:00
parent 62f08d38db
commit db0f0c462b
4 changed files with 105 additions and 29 deletions

View File

@ -43,7 +43,7 @@ import {
* var address0;
* var city1;
* var change;
* var changes = [];
* var changes = null;
* var temp;
* var context = this.context;
*
@ -60,14 +60,15 @@ import {
*
* city1 = address0.city;
* if (city1 !== this.city1) {
* changes.push(ChangeDetectionUtil.simpleChangeRecord(this.protos[1].bindingMemento, this.city1, city1));
* changes = ChangeDetectionUtil.addRecord(changes,
* ChangeDetectionUtil.simpleChangeRecord(this.protos[1].bindingMemento, this.city1, city1));
* this.city1 = city1;
* }
*
* if (changes.length > 0) {
* if(throwOnChange) ChangeDetectionUtil.throwOnChange(this.protos[1], changes[0]);
* this.dispatcher.onRecordChange('address.city', changes);
* changes = [];
* changes = null;
* }
* }
*
@ -144,7 +145,7 @@ ${localDefinitions}
${changeDefinitions}
var ${TEMP_LOCAL};
var ${CHANGE_LOCAL};
var ${CHANGES_LOCAL} = [];
var ${CHANGES_LOCAL} = null;
context = this.context;
${records}
@ -153,10 +154,10 @@ ${records}
function notifyTemplate(index:number):string{
return `
if (${CHANGES_LOCAL}.length > 0) {
if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
if(throwOnChange) ${UTIL}.throwOnChange(${PROTOS_ACCESSOR}[${index}], ${CHANGES_LOCAL}[0]);
${DISPATCHER_ACCESSOR}.onRecordChange(${PROTOS_ACCESSOR}[${index}].groupMemento, ${CHANGES_LOCAL});
${CHANGES_LOCAL} = [];
${CHANGES_LOCAL} = null;
}
`;
}
@ -166,7 +167,8 @@ function structuralCheckTemplate(selfIndex:number, field:string, context:string,
return `
${CHANGE_LOCAL} = ${UTIL}.structuralCheck(${field}, ${context});
if (${CHANGE_LOCAL}) {
${CHANGES_LOCAL}.push(${UTIL}.changeRecord(${PROTOS_ACCESSOR}[${selfIndex}].bindingMemento, ${CHANGE_LOCAL}));
${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
${UTIL}.changeRecord(${PROTOS_ACCESSOR}[${selfIndex}].bindingMemento, ${CHANGE_LOCAL}));
${field} = ${CHANGE_LOCAL}.currentValue;
}
${notify}
@ -222,7 +224,8 @@ if (${cond}) {
}
function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newValue:string) {
return `${CHANGES_LOCAL}.push(${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`;
return `${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`;
}

View File

@ -19,6 +19,72 @@ export class SimpleChange {
}
}
var _simpleChangesIndex = 0;
var _simpleChanges = [
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null)
]
var _changeRecordsIndex = 0;
var _changeRecords = [
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null),
new ChangeRecord(null, null)
]
function _simpleChange(previousValue, currentValue) {
var index = _simpleChangesIndex++ % 20;
var s = _simpleChanges[index];
s.previousValue = previousValue;
s.currentValue = currentValue;
return s;
}
function _changeRecord(bindingMemento, change) {
var index = _changeRecordsIndex++ % 20;
var s = _changeRecords[index];
s.bindingMemento = bindingMemento;
s.change = change;
return s;
}
var _singleElementList = [null];
export class ChangeDetectionUtil {
static unitialized() {
return uninitialized;
@ -128,11 +194,29 @@ export class ChangeDetectionUtil {
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
}
static simpleChange(previousValue:any, currentValue:any):SimpleChange {
return _simpleChange(previousValue, currentValue);
}
static changeRecord(memento:any, change:any):ChangeRecord {
return new ChangeRecord(memento, change);
return _changeRecord(memento, change);
}
static simpleChangeRecord(memento:any, previousValue:any, currentValue:any):ChangeRecord {
return new ChangeRecord(memento, new SimpleChange(previousValue, currentValue));
return _changeRecord(memento, _simpleChange(previousValue, currentValue));
}
}
static addRecord(updatedRecords:List, changeRecord:ChangeRecord):List {
if (isBlank(updatedRecords)) {
updatedRecords = _singleElementList;
updatedRecords[0] = changeRecord;
} else if (updatedRecords === _singleElementList) {
updatedRecords = [_singleElementList[0], changeRecord];
} else {
ListWrapper.push(updatedRecords, changeRecord);
}
return updatedRecords;
}
}

View File

@ -62,7 +62,8 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
if (isPresent(change)) {
currentGroup = proto.groupMemento;
updatedRecords = this._addRecord(updatedRecords, proto, change);
var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change);
updatedRecords = ChangeDetectionUtil.addRecord(updatedRecords, record);
}
if (proto.lastInGroup && isPresent(updatedRecords)) {
@ -100,7 +101,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
this._setChanged(proto, true);
if (proto.lastInBinding) {
return new SimpleChange(prevValue, currValue);
return ChangeDetectionUtil.simpleChange(prevValue, currValue);
} else {
return null;
}
@ -164,22 +165,6 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
return change;
}
_addRecord(updatedRecords:List, proto:ProtoRecord, change):List {
// we can use a pool of change records not to create extra garbage
var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change);
if (isBlank(updatedRecords)) {
updatedRecords = _singleElementList;
updatedRecords[0] = record;
} else if (updatedRecords === _singleElementList) {
updatedRecords = [_singleElementList[0], record];
} else {
ListWrapper.push(updatedRecords, record);
}
return updatedRecords;
}
_readContext(proto:ProtoRecord) {
return this.values[proto.contextIndex];
}

View File

@ -59,6 +59,10 @@ export function main() {
expect(dispatcher.log).toEqual(['name=misko']);
dispatcher.clear();
cd.detectChanges();
expect(dispatcher.log).toEqual([]);
dispatcher.clear();
person.name = "Misko";
cd.detectChanges();
expect(dispatcher.log).toEqual(['name=Misko']);