Skip to content

Instantly share code, notes, and snippets.

@rrousselGit
Last active August 1, 2020 05:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rrousselGit/5a047bd4ec36515a4cfcc6bd275f05f5 to your computer and use it in GitHub Desktop.
Save rrousselGit/5a047bd4ec36515a4cfcc6bd275f05f5 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>();
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');
}
}
}
}
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