Skip to content

Commit 4cadae0

Browse files
vicbrkirov
authored andcommitted
feat(dccd): add Support for ObservableList, ObservableMap & ChangeNotifier
Lower the target time to prevent a memory overflow. Benchmark added. Fixes dart-archive#773 Closes dart-archive#1156
1 parent fb7a3aa commit 4cadae0

10 files changed

+942
-331
lines changed

benchmark/_perf.dart

+10-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ time(name, body) {
88
print('$name: => ${statMeasure(body)}');
99
}
1010

11-
statMeasure(body) {
11+
StatSample statMeasure(body) {
1212
var list = [];
1313
var count = 100;
1414
for(var i = 0; i < count; i++) {
@@ -18,11 +18,11 @@ statMeasure(body) {
1818
return new StatSample(list);
1919
}
2020

21-
measure(b) {
21+
Sample measure(b) {
2222
// actual test;
2323
var count = 0;
2424
var stopwatch = new Stopwatch();
25-
var targetTime = 50 * 1000;
25+
var targetTime = 500;
2626
stopwatch.start();
2727
do {
2828
b();
@@ -49,6 +49,9 @@ measure(b) {
4949
b(); b(); b(); b(); b(); b(); b(); b(); b(); b(); // 8
5050
b(); b(); b(); b(); b(); b(); b(); b(); b(); b(); // 9
5151
}
52+
for(var i = 0; i < count % 100; i++) {
53+
b();
54+
}
5255
}
5356
stopwatch.stop();
5457
int elapsed = max(1, stopwatch.elapsedMicroseconds);
@@ -98,12 +101,12 @@ class StatSample {
98101
}
99102

100103
class Sample {
101-
num count;
102-
num time_us;
104+
final num count;
105+
final num time_us;
103106

104107
Sample(this.count, this.time_us);
105108

106-
get rate => count / time_us;
109+
num get rate => count / time_us;
107110

108-
toString() => rate;
111+
String toString() => '$rate';
109112
}

benchmark/watch_group_perf.dart

+228-43
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import 'package:angular/change_detection/dirty_checking_change_detector_dynamic.
66
import 'package:angular/change_detection/dirty_checking_change_detector_static.dart';
77
import 'package:angular/change_detection/watch_group.dart';
88
import 'package:benchmark_harness/benchmark_harness.dart';
9+
import 'package:observe/observe.dart';
10+
import 'package:observe/mirrors_used.dart';
11+
import "dart:io";
12+
import "dart:async";
13+
import "dart:convert";
914

1015
@MirrorsUsed(
1116
targets: const [
@@ -25,62 +30,135 @@ var _staticFieldGetterFactory = new StaticFieldGetterFactory({
2530
var _dynamicFieldGetterFactory = new DynamicFieldGetterFactory();
2631

2732
main() {
28-
_fieldRead();
29-
_fieldReadGetter();
33+
_collectionRead(observable:false, changeCount:0);
34+
_collectionRead(observable:true, changeCount:0);
35+
36+
_collectionRead(observable:false, changeCount:1);
37+
_collectionRead(observable:true, changeCount:1);
38+
39+
_collectionRead(observable:false, changeCount:10);
40+
_collectionRead(observable:true, changeCount:10);
41+
42+
_groupAddRemove(observable:false);
43+
_groupAddRemove(observable:true);
44+
45+
_fieldRead(observable:false, staticGetter: false, changeCount: 0);
46+
_fieldRead(observable:true, staticGetter: false, changeCount: 0);
47+
48+
_fieldRead(observable:false, staticGetter: false, changeCount: 1);
49+
_fieldRead(observable:true, staticGetter: false, changeCount: 1);
50+
51+
_fieldRead(observable:false, staticGetter: false, changeCount: 10);
52+
_fieldRead(observable:true, staticGetter: false, changeCount: 10);
53+
54+
_fieldRead(observable:false, staticGetter: true, changeCount: 0);
55+
_fieldRead(observable:true, staticGetter: true, changeCount: 0);
56+
57+
_fieldRead(observable:false, staticGetter: true, changeCount: 1);
58+
_fieldRead(observable:true, staticGetter: true, changeCount: 1);
59+
60+
_fieldRead(observable:false, staticGetter: true, changeCount: 10);
61+
_fieldRead(observable:true, staticGetter: true, changeCount: 10);
62+
3063
_mapRead();
3164
_methodInvoke0();
3265
_methodInvoke1();
3366
_function2();
34-
new _CollectionCheck().report();
3567
}
3668

37-
class _CollectionCheck extends BenchmarkBase {
38-
List<int> list = new List.generate(1000, (i) => i);
69+
void _collectionRead({bool observable, int changeCount}) {
3970
var detector = new DirtyCheckingChangeDetector(_dynamicFieldGetterFactory);
4071

41-
_CollectionCheck(): super('change-detect List[1000]') {
42-
detector
43-
..watch(list, null, 'handler')
44-
..collectChanges(); // intialize
45-
}
72+
var description = '';
4673

47-
run() {
48-
detector.collectChanges();
74+
List<int> list = new List.generate(20, (i) => i);
75+
76+
if (observable) {
77+
list = new ObservableList<int>.from(list);
78+
description += '[CHANGE NOTIFIER]';
79+
} else {
80+
description += '[DIRTY CHECK]';
81+
}
82+
if (changeCount == 0) {
83+
description += ' 0/${list.length} change';
84+
} else if (changeCount == 1) {
85+
description += ' 1/${list.length} change';
86+
} else if (changeCount > 1) {
87+
description += ' $changeCount/${list.length} changes';
4988
}
50-
}
5189

52-
_fieldRead() {
53-
var watchGrp = new RootWatchGroup(_dynamicFieldGetterFactory,
54-
new DirtyCheckingChangeDetector(_dynamicFieldGetterFactory), new _Obj())
55-
..watch(_parse('a'), _reactionFn)
56-
..watch(_parse('b'), _reactionFn)
57-
..watch(_parse('c'), _reactionFn)
58-
..watch(_parse('d'), _reactionFn)
59-
..watch(_parse('e'), _reactionFn)
60-
..watch(_parse('f'), _reactionFn)
61-
..watch(_parse('g'), _reactionFn)
62-
..watch(_parse('h'), _reactionFn)
63-
..watch(_parse('i'), _reactionFn)
64-
..watch(_parse('j'), _reactionFn)
65-
..watch(_parse('k'), _reactionFn)
66-
..watch(_parse('l'), _reactionFn)
67-
..watch(_parse('m'), _reactionFn)
68-
..watch(_parse('n'), _reactionFn)
69-
..watch(_parse('o'), _reactionFn)
70-
..watch(_parse('p'), _reactionFn)
71-
..watch(_parse('q'), _reactionFn)
72-
..watch(_parse('r'), _reactionFn)
73-
..watch(_parse('s'), _reactionFn)
74-
..watch(_parse('t'), _reactionFn);
90+
var watch = detector.watch(list, null, 'handler');
91+
detector.collectChanges();
7592

76-
print('Watch: ${watchGrp.fieldCost}; eval: ${watchGrp.evalCost}');
93+
time(description + (observable ? ' ObservableList' : ' List'), () {
94+
detector.collectChanges();
95+
if (changeCount == 1) {
96+
list[3]++;
97+
} else if (changeCount > 1) {
98+
for (var i = 0; i < list.length; i++) {
99+
list[i]++;
100+
}
101+
}
102+
});
103+
}
77104

78-
time('fieldRead', () => watchGrp.detectChanges());
105+
void _groupAddRemove({bool observable}) {
106+
var description = '';
107+
if (observable) {
108+
description += '[CHANGE NOTIFIER]';
109+
} else {
110+
description += '[DIRTY CHECK]';
111+
}
112+
var rootWatchGrp = new RootWatchGroup(_staticFieldGetterFactory,
113+
new DirtyCheckingChangeDetector(_staticFieldGetterFactory), {});
114+
var testRun = () {
115+
for (int i = 0; i < 10; i++) {
116+
WatchGroup child = rootWatchGrp.newGroup(observable ? new _ObservableObj() : new _Obj())
117+
..watch(_parse('a'), _reactionFn)
118+
..watch(_parse('b'), _reactionFn)
119+
..watch(_parse('c'), _reactionFn)
120+
..watch(_parse('d'), _reactionFn)
121+
..watch(_parse('e'), _reactionFn)
122+
..watch(_parse('f'), _reactionFn)
123+
..watch(_parse('g'), _reactionFn)
124+
..watch(_parse('h'), _reactionFn)
125+
..watch(_parse('i'), _reactionFn)
126+
..watch(_parse('j'), _reactionFn);
127+
rootWatchGrp.detectChanges();
128+
child.remove();
129+
}
130+
131+
};
132+
time(description + ' add/remove 10 watchGroups with 10 watches', testRun);
79133
}
80134

81-
_fieldReadGetter() {
82-
var watchGrp= new RootWatchGroup(_staticFieldGetterFactory,
83-
new DirtyCheckingChangeDetector(_staticFieldGetterFactory), new _Obj())
135+
void _fieldRead({bool observable, bool staticGetter, int changeCount}) {
136+
var description = '';
137+
var obj;
138+
if (observable) {
139+
description += '[CHANGE NOTIFIER]';
140+
obj = new _ObservableObj();
141+
} else {
142+
description += '[DIRTY CHECK]';
143+
obj = new _Obj();
144+
}
145+
var fieldGetterFactory;
146+
if (staticGetter) {
147+
description += ' staticGetter';
148+
fieldGetterFactory = _staticFieldGetterFactory;
149+
} else {
150+
description += ' dynamicGetter';
151+
fieldGetterFactory = _dynamicFieldGetterFactory;
152+
}
153+
if (changeCount == 0) {
154+
description +=' 0/20 field change';
155+
} else if (changeCount == 1) {
156+
description +=' 1/20 field change';
157+
} else if (changeCount > 1) {
158+
description +=' 20/20 field changes';
159+
}
160+
var watchGrp = new RootWatchGroup(fieldGetterFactory,
161+
new DirtyCheckingChangeDetector(fieldGetterFactory), obj)
84162
..watch(_parse('a'), _reactionFn)
85163
..watch(_parse('b'), _reactionFn)
86164
..watch(_parse('c'), _reactionFn)
@@ -104,7 +182,33 @@ _fieldReadGetter() {
104182

105183
print('Watch: ${watchGrp.fieldCost}; eval: ${watchGrp.evalCost}');
106184

107-
time('fieldReadGetter', () => watchGrp.detectChanges());
185+
time(description + ' on the same object', () {
186+
if (changeCount == 1) {
187+
obj.c++;
188+
} else if (changeCount > 1) {
189+
obj.a++;
190+
obj.b++;
191+
obj.c++;
192+
obj.d++;
193+
obj.e++;
194+
obj.f++;
195+
obj.g++;
196+
obj.h++;
197+
obj.i++;
198+
obj.j++;
199+
obj.k++;
200+
obj.l++;
201+
obj.m++;
202+
obj.n++;
203+
obj.o++;
204+
obj.p++;
205+
obj.q++;
206+
obj.r++;
207+
obj.s++;
208+
obj.t++;
209+
}
210+
watchGrp.detectChanges();
211+
});
108212
}
109213

110214
_mapRead() {
@@ -313,5 +417,86 @@ class _Obj {
313417
methodR() => r;
314418
methodS() => s;
315419
methodT() => t;
316-
317420
}
421+
422+
class _ObservableObj extends ChangeNotifier {
423+
@reflectable @observable dynamic get a => __$a;
424+
dynamic __$a = 1;
425+
@reflectable set a(dynamic value) { __$a = notifyPropertyChange(#a, __$a, value); }
426+
427+
@reflectable @observable dynamic get b => __$b;
428+
dynamic __$b = 2;
429+
@reflectable set b(dynamic value) { __$b = notifyPropertyChange(#b, __$b, value); }
430+
431+
@reflectable @observable dynamic get c => __$c;
432+
dynamic __$c = 3;
433+
@reflectable set c(dynamic value) { __$c = notifyPropertyChange(#c, __$c, value); }
434+
435+
@reflectable @observable dynamic get d => __$d;
436+
dynamic __$d = 4;
437+
@reflectable set d(dynamic value) { __$d = notifyPropertyChange(#d, __$d, value); }
438+
439+
@reflectable @observable dynamic get e => __$e;
440+
dynamic __$e = 5;
441+
@reflectable set e(dynamic value) { __$e = notifyPropertyChange(#e, __$e, value); }
442+
443+
@reflectable @observable dynamic get f => __$f;
444+
dynamic __$f = 6;
445+
@reflectable set f(dynamic value) { __$f = notifyPropertyChange(#f, __$f, value); }
446+
447+
@reflectable @observable dynamic get g => __$g;
448+
dynamic __$g = 7;
449+
@reflectable set g(dynamic value) { __$g = notifyPropertyChange(#g, __$g, value); }
450+
451+
@reflectable @observable dynamic get h => __$h;
452+
dynamic __$h = 8;
453+
@reflectable set h(dynamic value) { __$h = notifyPropertyChange(#h, __$h, value); }
454+
455+
@reflectable @observable dynamic get i => __$i;
456+
dynamic __$i = 9;
457+
@reflectable set i(dynamic value) { __$i = notifyPropertyChange(#i, __$i, value); }
458+
459+
@reflectable @observable dynamic get j => __$j;
460+
dynamic __$j = 10;
461+
@reflectable set j(dynamic value) { __$j = notifyPropertyChange(#j, __$j, value); }
462+
463+
@reflectable @observable dynamic get k => __$k;
464+
dynamic __$k = 11;
465+
@reflectable set k(dynamic value) { __$k = notifyPropertyChange(#k, __$k, value); }
466+
467+
@reflectable @observable dynamic get l => __$l;
468+
dynamic __$l = 12;
469+
@reflectable set l(dynamic value) { __$l = notifyPropertyChange(#l, __$l, value); }
470+
471+
@reflectable @observable dynamic get m => __$m;
472+
dynamic __$m = 13;
473+
@reflectable set m(dynamic value) { __$m = notifyPropertyChange(#m, __$m, value); }
474+
475+
@reflectable @observable dynamic get n => __$n;
476+
dynamic __$n = 14;
477+
@reflectable set n(dynamic value) { __$n = notifyPropertyChange(#n, __$n, value); }
478+
479+
@reflectable @observable dynamic get o => __$o;
480+
dynamic __$o = 15;
481+
@reflectable set o(dynamic value) { __$o = notifyPropertyChange(#o, __$o, value); }
482+
483+
@reflectable @observable dynamic get p => __$p;
484+
dynamic __$p = 16;
485+
@reflectable set p(dynamic value) { __$p = notifyPropertyChange(#p, __$p, value); }
486+
487+
@reflectable @observable dynamic get q => __$q;
488+
dynamic __$q = 17;
489+
@reflectable set q(dynamic value) { __$q = notifyPropertyChange(#q, __$q, value); }
490+
491+
@reflectable @observable dynamic get r => __$r;
492+
dynamic __$r = 18;
493+
@reflectable set r(dynamic value) { __$r = notifyPropertyChange(#r, __$r, value); }
494+
495+
@reflectable @observable dynamic get s => __$s;
496+
dynamic __$s = 19;
497+
@reflectable set s(dynamic value) { __$s = notifyPropertyChange(#s, __$s, value); }
498+
499+
@reflectable @observable dynamic get t => __$t;
500+
dynamic __$t = 20;
501+
@reflectable set t(dynamic value) { __$t = notifyPropertyChange(#t, __$t, value); }
502+
}

example/pubspec.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ dependencies:
99

1010
transformers:
1111
- angular
12+

0 commit comments

Comments
 (0)