Last active
August 1, 2020 05:43
-
-
Save rrousselGit/5a047bd4ec36515a4cfcc6bd275f05f5 to your computer and use it in GitHub Desktop.
A benchmark that compares before and after performances for notifyListeners of ChangeNotifier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'observer_list.dart'; | |
typedef VoidCallback = void Function(); | |
class ChangeNotifier { | |
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>(); | |
bool get hasListeners { | |
return _listeners.isNotEmpty; | |
} | |
void addListener(VoidCallback listener) { | |
_listeners.add(listener); | |
} | |
void removeListener(VoidCallback listener) { | |
_listeners.remove(listener); | |
} | |
void dispose() { | |
_listeners = null; | |
} | |
void notifyListeners() { | |
if (_listeners != null) { | |
final localListeners = List<VoidCallback>.from(_listeners); | |
for (final listener in localListeners) { | |
try { | |
if (_listeners.contains(listener)) listener(); | |
} catch (exception, stack) { | |
print('error'); | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:collection'; | |
class _ListenerEntry extends LinkedListEntry<_ListenerEntry> { | |
_ListenerEntry(this.listener); | |
final void Function() listener; | |
} | |
class ChangeNotifier { | |
LinkedList<_ListenerEntry> _listeners = LinkedList<_ListenerEntry>(); | |
bool get hasListeners { | |
return _listeners.isNotEmpty; | |
} | |
void addListener(void Function() listener) { | |
_listeners.add(_ListenerEntry(listener)); | |
} | |
@override | |
void removeListener(void Function() listener) { | |
for (final _ListenerEntry entry in _listeners) { | |
if (entry.listener == listener) { | |
entry.unlink(); | |
return; | |
} | |
} | |
} | |
void dispose() { | |
_listeners = null; | |
} | |
void notifyListeners() { | |
if (_listeners.isEmpty) return; | |
final List<_ListenerEntry> listeners = _listeners.toList(); | |
for (final _ListenerEntry entry in listeners) { | |
try { | |
if (entry.list != null) | |
entry.listener(); | |
} catch (exception, stack) { | |
print('error'); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:benchmark_harness/benchmark_harness.dart'; | |
import 'change_notifier.dart'; | |
import 'change_notifier2.dart' as new_notifier; | |
class MyChangeNotifier extends ChangeNotifier { | |
int _value; | |
int get value => _value; | |
set value(int value) { | |
_value = value; | |
notifyListeners(); | |
} | |
} | |
class MyNewChangeNotifier extends new_notifier.ChangeNotifier { | |
int _value; | |
int get value => _value; | |
set value(int value) { | |
_value = value; | |
notifyListeners(); | |
} | |
} | |
class ChangeNotifierBenchmark extends BenchmarkBase { | |
ChangeNotifierBenchmark(this.listenerCount) | |
: super('ChangeNotifier(listenerCount: $listenerCount)'); | |
final int listenerCount; | |
MyChangeNotifier notifier; | |
@override | |
void run() { | |
for (var i = 0; i < 100; i++) { | |
notifier.value = i; | |
} | |
} | |
@override | |
void setup() { | |
notifier = MyChangeNotifier(); | |
for (var i = 0; i < listenerCount; i++) { | |
notifier.addListener(() {}); | |
} | |
} | |
@override | |
void teardown() { | |
notifier.dispose(); | |
notifier = null; | |
} | |
} | |
class NewChangeNotifierBenchmark extends BenchmarkBase { | |
NewChangeNotifierBenchmark(this.listenerCount) | |
: super('New change notifier (listenerCount: $listenerCount)'); | |
final int listenerCount; | |
MyNewChangeNotifier notifier; | |
@override | |
void run() { | |
for (var i = 0; i < 100; i++) { | |
notifier.value = i; | |
} | |
} | |
@override | |
void setup() { | |
notifier = MyNewChangeNotifier(); | |
for (var i = 0; i < listenerCount; i++) { | |
notifier.addListener(() { }); | |
} | |
} | |
@override | |
void teardown() { | |
notifier.dispose(); | |
notifier = null; | |
} | |
} | |
main() { | |
ChangeNotifierBenchmark(0).report(); | |
ChangeNotifierBenchmark(1).report(); | |
ChangeNotifierBenchmark(2).report(); | |
ChangeNotifierBenchmark(3).report(); | |
ChangeNotifierBenchmark(4).report(); | |
ChangeNotifierBenchmark(5).report(); | |
print(''); | |
NewChangeNotifierBenchmark(0).report(); | |
NewChangeNotifierBenchmark(1).report(); | |
NewChangeNotifierBenchmark(2).report(); | |
NewChangeNotifierBenchmark(3).report(); | |
NewChangeNotifierBenchmark(4).report(); | |
NewChangeNotifierBenchmark(5).report(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:collection'; | |
class ObserverList<T> extends Iterable<T> { | |
final List<T> _list = <T>[]; | |
bool _isDirty = false; | |
HashSet<T> _set; | |
/// Adds an item to the end of this list. | |
/// | |
/// This operation has constant time complexity. | |
void add(T item) { | |
_isDirty = true; | |
_list.add(item); | |
} | |
/// Removes an item from the list. | |
/// | |
/// This is O(N) in the number of items in the list. | |
/// | |
/// Returns whether the item was present in the list. | |
bool remove(T item) { | |
_isDirty = true; | |
_set?.clear(); // Clear the set so that we don't leak items. | |
return _list.remove(item); | |
} | |
@override | |
bool contains(Object element) { | |
if (_list.length < 3) | |
return _list.contains(element); | |
if (_isDirty) { | |
if (_set == null) { | |
_set = HashSet<T>.from(_list); | |
} else { | |
_set.addAll(_list); | |
} | |
_isDirty = false; | |
} | |
return _set.contains(element); | |
} | |
@override | |
Iterator<T> get iterator => _list.iterator; | |
@override | |
bool get isEmpty => _list.isEmpty; | |
@override | |
bool get isNotEmpty => _list.isNotEmpty; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment