OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 library observe.test.benchmark.observation_benchmark_base; | |
5 | |
6 import 'dart:async'; | |
7 import 'dart:html'; | |
8 import 'package:observe/observe.dart'; | |
9 import 'package:benchmark_harness/benchmark_harness.dart'; | |
10 | |
11 abstract class ObservationBenchmarkBase extends BenchmarkBase { | |
12 /// The number of objects to create and observe. | |
13 final int objectCount; | |
14 | |
15 /// The number of mutations to perform. | |
16 final int mutationCount; | |
17 | |
18 /// The current configuration. | |
19 final String config; | |
20 | |
21 /// The number of pending mutations left to observe. | |
22 int mutations; | |
23 | |
24 /// The objects we want to observe. | |
25 List<Observable> objects; | |
26 | |
27 /// The change listeners on all of our objects. | |
28 List observers; | |
29 | |
30 /// The current object being mutated. | |
31 int objectIndex; | |
32 | |
33 ObservationBenchmarkBase( | |
34 String name, this.objectCount, this.mutationCount, this.config) | |
35 : super(name); | |
36 | |
37 /// Subclasses should use this method to perform mutations on an object. The | |
38 /// return value indicates how many mutations were performed on the object. | |
39 int mutateObject(obj); | |
40 | |
41 /// Subclasses should use this method to return an observable object to be | |
42 /// benchmarked. | |
43 Observable newObject(); | |
44 | |
45 /// Subclasses should override this to do anything other than a default change | |
46 /// listener. It must return either a StreamSubscription or a PathObserver. | |
47 /// If overridden this observer should decrement [mutations] each time a | |
48 /// change is observed. | |
49 newObserver(obj) { | |
50 decrement(_) => mutations--; | |
51 if (obj is ObservableList) return obj.listChanges.listen(decrement); | |
52 return obj.changes.listen(decrement); | |
53 } | |
54 | |
55 /// Set up each benchmark by creating all the objects and listeners. | |
56 @override | |
57 void setup() { | |
58 mutations = 0; | |
59 | |
60 objects = []; | |
61 observers = []; | |
62 objectIndex = 0; | |
63 | |
64 while (objects.length < objectCount) { | |
65 var obj = newObject(); | |
66 objects.add(obj); | |
67 observers.add(newObserver(obj)); | |
68 } | |
69 } | |
70 | |
71 /// Tear down each benchmark and make sure that [mutations] is 0. | |
72 @override | |
73 void teardown() { | |
74 if (mutations != 0) { | |
75 window.alert('$mutations mutation sets were not observed!'); | |
76 } | |
77 mutations = 0; | |
78 | |
79 while (observers.isNotEmpty) { | |
80 var observer = observers.removeLast(); | |
81 if (observer is StreamSubscription) { | |
82 observer.cancel(); | |
83 } else if (observer is PathObserver) { | |
84 observer.close(); | |
85 } else { | |
86 throw 'Unknown observer type ${observer.runtimeType}. Only ' | |
87 '[PathObserver] and [StreamSubscription] are supported.'; | |
88 } | |
89 } | |
90 observers = null; | |
91 | |
92 bool leakedObservers = false; | |
93 while (objects.isNotEmpty) { | |
94 leakedObservers = objects.removeLast().hasObservers || leakedObservers; | |
95 } | |
96 if (leakedObservers) window.alert('Observers leaked!'); | |
97 objects = null; | |
98 } | |
99 | |
100 /// Run the benchmark | |
101 @override | |
102 void run() { | |
103 var mutationsLeft = mutationCount; | |
104 while (mutationsLeft > 0) { | |
105 var obj = objects[objectIndex]; | |
106 mutationsLeft -= mutateObject(obj); | |
107 this.mutations++; | |
108 this.objectIndex++; | |
109 if (this.objectIndex == this.objects.length) { | |
110 this.objectIndex = 0; | |
111 } | |
112 obj.deliverChanges(); | |
113 if (obj is ObservableList) obj.deliverListChanges(); | |
114 } | |
115 Observable.dirtyCheck(); | |
116 } | |
117 } | |
OLD | NEW |