Skip to content

Instantly share code, notes, and snippets.

@sachaarbonel
Forked from rrousselGit/change_notifier.dart
Created July 28, 2020 20:53
Show Gist options
  • Save sachaarbonel/0d9dd93ede9db9bc362f72217057bb5c to your computer and use it in GitHub Desktop.
Save sachaarbonel/0d9dd93ede9db9bc362f72217057bb5c to your computer and use it in GitHub Desktop.
A benchmark that compares before and after performances for notifyListeners of ChangeNotifier
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');
}
}
}
}
}
import 'dart:collection';
class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
_ListenerEntry(this.listener);
final void Function() listener;
}
class ChangeNotifier {
LinkedList<_ListenerEntry> _listeners = LinkedList<_ListenerEntry>();
_ListenerEntry _firstNewlyAddedEntry;
bool get hasListeners {
return _listeners.isNotEmpty;
}
@override
void addListener(void Function() listener) {
final _ListenerEntry entry = _ListenerEntry(listener);
_firstNewlyAddedEntry ??= entry;
_listeners.add(entry);
}
@override
void removeListener(void Function() listener) {
_listeners
.firstWhere(
(_ListenerEntry element) => element.listener == listener,
orElse: () => null,
)
?.unlink();
}
void dispose() {
_listeners = null;
}
void notifyListeners() {
_firstNewlyAddedEntry = null;
if (_listeners != null && _listeners.isNotEmpty) {
for (_ListenerEntry entry = _listeners.first;
entry != null && entry != _firstNewlyAddedEntry;
entry = entry.next) {
try {
entry.listener();
} catch (exception, stack) {
print('error');
}
}
}
}
}
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();
}
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