Skip to content

Commit 65242fb

Browse files
committed
perf(change_detection): add baseline to change detection benchmark
1 parent 847cefc commit 65242fb

File tree

4 files changed

+165
-48
lines changed

4 files changed

+165
-48
lines changed

modules/benchmarks/src/change_detection/benchmark.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,5 @@ import './change_detection_benchmark.dart' as cdb;
44
import 'dart:js' as js;
55

66
main () {
7-
js.context['benchmarkSteps'].add(new js.JsObject.jsify({
8-
"name": "Change Detection",
9-
"fn": new js.JsFunction.withThis((_) => cdb.run())
10-
}));
7+
cdb.run();
118
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
System.import('benchmarks/change_detection/change_detection_benchmark').then(function (bm) {
2-
window.benchmarkSteps.push({name: 'ChangeDetection', fn: bm.run});
2+
bm.run();
33
}, console.log.bind(console));

modules/benchmarks/src/change_detection/change_detection_benchmark.js

Lines changed: 154 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {ListWrapper, MapWrapper} from 'facade/collection';
22
import {Parser} from 'change_detection/parser/parser';
33
import {Lexer} from 'change_detection/parser/lexer';
44
import {reflector} from 'reflection/reflection';
5+
import {isPresent} from 'facade/lang';
6+
import {benchmark, benchmarkStep} from '../benchpress';
57

68
import {
79
ChangeDetector,
@@ -10,51 +12,175 @@ import {
1012
} from 'change_detection/change_detector';
1113

1214

13-
var ITERATIONS = 100000;
15+
var ITERATIONS = 200000;
1416

15-
export function run () {
17+
class Obj {
18+
field0;
19+
field1;
20+
field2;
21+
field3;
22+
field4;
23+
field5;
24+
field6;
25+
field7;
26+
field8;
27+
field9;
28+
29+
30+
setField(index, value) {
31+
switch (index) {
32+
case 0: this.field0 = value; break;
33+
case 1: this.field1 = value; break;
34+
case 2: this.field2 = value; break;
35+
case 3: this.field3 = value; break;
36+
case 4: this.field4 = value; break;
37+
case 5: this.field5 = value; break;
38+
case 6: this.field6 = value; break;
39+
case 7: this.field7 = value; break;
40+
case 8: this.field8 = value; break;
41+
case 9: this.field9 = value; break;
42+
}
43+
}
44+
}
45+
46+
class Row {
47+
previousValue;
48+
obj;
49+
getter;
50+
next;
51+
}
52+
53+
function setUpReflector() {
1654
reflector.registerGetters({
17-
'a': function(obj){return obj.a},
18-
'b': function(obj){return obj.b},
19-
'c': function(obj){return obj.c}
55+
'field0': function(obj){return obj.field0},
56+
'field1': function(obj){return obj.field1},
57+
'field2': function(obj){return obj.field2},
58+
'field3': function(obj){return obj.field3},
59+
'field4': function(obj){return obj.field4},
60+
'field5': function(obj){return obj.field5},
61+
'field6': function(obj){return obj.field6},
62+
'field7': function(obj){return obj.field7},
63+
'field8': function(obj){return obj.field8},
64+
'field9': function(obj){return obj.field9}
2065
});
21-
2266
reflector.registerSetters({
23-
'a': function(obj, v){return obj.a = v},
24-
'b': function(obj, v){return obj.b = v},
25-
'c': function(obj, v){return obj.c = v}
67+
'field0': function(obj, v){return obj.field0 = v},
68+
'field1': function(obj, v){return obj.field1 = v},
69+
'field2': function(obj, v){return obj.field2 = v},
70+
'field3': function(obj, v){return obj.field3 = v},
71+
'field4': function(obj, v){return obj.field4 = v},
72+
'field5': function(obj, v){return obj.field5 = v},
73+
'field6': function(obj, v){return obj.field6 = v},
74+
'field7': function(obj, v){return obj.field7 = v},
75+
'field8': function(obj, v){return obj.field8 = v},
76+
'field9': function(obj, v){return obj.field9 = v}
2677
});
78+
}
2779

28-
var parser = new Parser(new Lexer());
29-
var astWithSource = parser.parseBinding('a + b * c');
80+
function setUpBaseline() {
81+
function createRow(i) {
82+
var obj = new Obj();
83+
var index = i % 10;
84+
obj.setField(index, i);
3085

31-
var prr = new ProtoRecordRange();
32-
prr.addRecordsFromAST(astWithSource.ast, 'memo', false);
86+
var r = new Row();
87+
r.obj = obj;
88+
r.previousValue = i;
89+
r.getter = reflector.getter(`field${index}`);
90+
return r;
91+
}
3392

93+
var head = createRow(0);
94+
var current = head;
95+
for (var i = 1; i < ITERATIONS; i++) {
96+
var newRow = createRow(i);
97+
current.next = newRow;
98+
current = newRow;
99+
}
100+
return head;
101+
}
102+
103+
function setUpChangeDetection() {
34104
var dispatcher = new DummyDispatcher();
35-
var rr = prr.instantiate(dispatcher, MapWrapper.create());
36-
rr.setContext(new Component());
105+
var parser = new Parser(new Lexer());
106+
107+
var parentProto = new ProtoRecordRange();
108+
var parentRange = parentProto.instantiate(dispatcher, MapWrapper.create());
109+
110+
var astWithSource = [
111+
parser.parseBinding('field0'),
112+
parser.parseBinding('field1'),
113+
parser.parseBinding('field2'),
114+
parser.parseBinding('field3'),
115+
parser.parseBinding('field4'),
116+
parser.parseBinding('field5'),
117+
parser.parseBinding('field6'),
118+
parser.parseBinding('field7'),
119+
parser.parseBinding('field8'),
120+
parser.parseBinding('field9')
121+
];
122+
123+
function proto(i) {
124+
var prr = new ProtoRecordRange();
125+
prr.addRecordsFromAST(astWithSource[i % 10].ast, "memo", i, false);
126+
return prr;
127+
}
128+
129+
var prr = [
130+
proto(0),
131+
proto(1),
132+
proto(2),
133+
proto(3),
134+
proto(4),
135+
proto(5),
136+
proto(6),
137+
proto(7),
138+
proto(8),
139+
proto(9)
140+
];
37141

38-
var cd = new ChangeDetector(rr);
39142
for (var i = 0; i < ITERATIONS; ++i) {
40-
cd.detectChanges();
143+
var obj = new Obj();
144+
var index = i % 10;
145+
obj.setField(index, i);
146+
147+
var rr = prr[index].instantiate(dispatcher, null);
148+
rr.setContext(obj);
149+
150+
parentRange.addRange(rr);
41151
}
152+
153+
return new ChangeDetector(parentRange);
42154
}
43155

156+
export function run () {
157+
setUpReflector();
44158

45-
class DummyDispatcher extends WatchGroupDispatcher {
46-
onRecordChange(record, context) {
47-
}
159+
benchmark(`Baseline`, function () {
160+
var head = setUpBaseline();
161+
162+
benchmarkStep('run', function () {
163+
var current = head;
164+
while (isPresent(current)) {
165+
if (current.getter(current.obj) !== current.previousValue) {
166+
throw "should not happen";
167+
}
168+
current = current.next;
169+
}
170+
});
171+
});
172+
173+
benchmark(`Change Detection`, function() {
174+
var cd = setUpChangeDetection();
175+
176+
benchmarkStep('run', function() {
177+
cd.detectChanges();
178+
});
179+
});
48180
}
49181

50-
class Component {
51-
a:number;
52-
b:number;
53-
c:number;
54182

55-
constructor() {
56-
this.a = 1;
57-
this.b = 2;
58-
this.c = 3;
183+
class DummyDispatcher extends WatchGroupDispatcher {
184+
onRecordChange(record, context) {
59185
}
60186
}

modules/change_detection/src/change_detector.js

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@ export class ChangeDetector {
2222
var currentRange = record.recordRange;
2323
var currentGroup = record.groupMemento();
2424

25-
var nextEnabled = record.nextEnabled;
26-
var nextRange = isPresent(nextEnabled) ? nextEnabled.recordRange : null;
27-
var nextGroup = isPresent(nextEnabled) ? nextEnabled.groupMemento() : null;
28-
2925
if (record.check()) {
3026
count ++;
3127
if (record.terminatesExpression()) {
3228
updatedRecords = this._addRecord(updatedRecords, record);
3329
}
3430
}
3531

36-
if (this._shouldNotifyDispatcher(currentRange, nextRange, currentGroup, nextGroup, updatedRecords)) {
37-
currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords);
38-
updatedRecords = null;
32+
if (isPresent(updatedRecords)) {
33+
var nextEnabled = record.nextEnabled;
34+
var nextRange = isPresent(nextEnabled) ? nextEnabled.recordRange : null;
35+
var nextGroup = isPresent(nextEnabled) ? nextEnabled.groupMemento() : null;
36+
37+
if (currentRange != nextRange || currentGroup != nextGroup) {
38+
currentRange.dispatcher.onRecordChange(currentGroup, updatedRecords);
39+
updatedRecords = null;
40+
}
3941
}
4042

4143
record = record.nextEnabled;
@@ -44,14 +46,6 @@ export class ChangeDetector {
4446
return count;
4547
}
4648

47-
_groupChanged(currentRange, nextRange, currentGroup, nextGroup) {
48-
return currentRange != nextRange || currentGroup != nextGroup;
49-
}
50-
51-
_shouldNotifyDispatcher(currentRange, nextRange, currentGroup, nextGroup, updatedRecords) {
52-
return this._groupChanged(currentRange, nextRange, currentGroup, nextGroup) && isPresent(updatedRecords);
53-
}
54-
5549
_addRecord(updatedRecords:List, record:Record) {
5650
if (isBlank(updatedRecords)) {
5751
updatedRecords = _singleElementList;

0 commit comments

Comments
 (0)