Add removeListenerWhileNotifying benchmark for ChangeNotifier (#71986)
This commit is contained in:
parent
dcc4fdd558
commit
831ee22b06
@ -6,108 +6,177 @@ import 'package:flutter/foundation.dart';
|
|||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
|
|
||||||
const int _kNumIterations = 1000;
|
const int _kNumIterations = 65536;
|
||||||
const double _scale = 1000.0 / _kNumIterations;
|
|
||||||
const int _kNumWarmUp = 100;
|
const int _kNumWarmUp = 100;
|
||||||
|
const int _kScale = 1000;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
assert(false, "Don't run benchmarks in checked mode! Use 'flutter run --release'.");
|
assert(false, "Don't run benchmarks in checked mode! Use 'flutter run --release'.");
|
||||||
|
|
||||||
void listener() {}
|
// In the following benchmarks, we won't remove the listeners when we don't
|
||||||
void listener2() {}
|
// want to measure removeListener because we know that everything will be
|
||||||
void listener3() {}
|
// GC'ed in the end.
|
||||||
void listener4() {}
|
// Not removing listeners would cause memory leaks in a real application.
|
||||||
void listener5() {}
|
|
||||||
|
|
||||||
// Warm up lap
|
|
||||||
for (int i = 0; i < _kNumWarmUp; i += 1) {
|
|
||||||
_Notifier()
|
|
||||||
..addListener(listener)
|
|
||||||
..addListener(listener2)
|
|
||||||
..addListener(listener3)
|
|
||||||
..addListener(listener4)
|
|
||||||
..addListener(listener5)
|
|
||||||
..notify()
|
|
||||||
..removeListener(listener)
|
|
||||||
..removeListener(listener2)
|
|
||||||
..removeListener(listener3)
|
|
||||||
..removeListener(listener4)
|
|
||||||
..removeListener(listener5);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Stopwatch addListenerWatch = Stopwatch();
|
|
||||||
final Stopwatch removeListenerWatch = Stopwatch();
|
|
||||||
final Stopwatch notifyListenersWatch = Stopwatch();
|
|
||||||
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
|
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
|
||||||
|
|
||||||
for (int listenersCount = 0; listenersCount <= 5; listenersCount++) {
|
void runAddListenerBenchmark(int iteration, {bool addResult = true}) {
|
||||||
|
const String name = 'addListener';
|
||||||
|
for (int listenerCount = 1; listenerCount <= 5; listenerCount += 1) {
|
||||||
|
final List<_Notifier> notifiers = List<_Notifier>.generate(
|
||||||
|
iteration,
|
||||||
|
(_) => _Notifier(),
|
||||||
|
growable: false,
|
||||||
|
);
|
||||||
|
|
||||||
for (int j = 0; j < _kNumIterations; j += 1) {
|
final Stopwatch watch = Stopwatch();
|
||||||
final _Notifier notifier = _Notifier();
|
watch.start();
|
||||||
addListenerWatch.start();
|
for (int i = 0; i < iteration; i += 1) {
|
||||||
|
for (int l = 0; l < listenerCount; l += 1) {
|
||||||
notifier.addListener(listener);
|
notifiers[i].addListener(() {});
|
||||||
if (listenersCount > 1)
|
}
|
||||||
notifier.addListener(listener2);
|
}
|
||||||
if (listenersCount > 2)
|
watch.stop();
|
||||||
notifier.addListener(listener3);
|
final int elapsed = watch.elapsedMicroseconds;
|
||||||
if (listenersCount > 3)
|
final double averagePerIteration = elapsed / iteration;
|
||||||
notifier.addListener(listener4);
|
if (addResult)
|
||||||
if (listenersCount > 4)
|
printer.addResult(
|
||||||
notifier.addListener(listener5);
|
description: '$name ($listenerCount listeners)',
|
||||||
|
value: averagePerIteration * _kScale,
|
||||||
addListenerWatch.stop();
|
unit: 'ns per iteration',
|
||||||
notifyListenersWatch.start();
|
name: '$name${listenerCount}_iteration',
|
||||||
|
);
|
||||||
notifier.notify();
|
|
||||||
|
|
||||||
notifyListenersWatch.stop();
|
|
||||||
removeListenerWatch.start();
|
|
||||||
|
|
||||||
// Remove listeners in reverse order to evaluate the worse-case scenario:
|
|
||||||
// the listener removed is the last listener
|
|
||||||
if (listenersCount > 4)
|
|
||||||
notifier.removeListener(listener5);
|
|
||||||
if (listenersCount > 3)
|
|
||||||
notifier.removeListener(listener4);
|
|
||||||
if (listenersCount > 2)
|
|
||||||
notifier.removeListener(listener3);
|
|
||||||
if (listenersCount > 1)
|
|
||||||
notifier.removeListener(listener2);
|
|
||||||
notifier.removeListener(listener);
|
|
||||||
|
|
||||||
removeListenerWatch.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final int notifyListener = notifyListenersWatch.elapsedMicroseconds;
|
|
||||||
notifyListenersWatch.reset();
|
|
||||||
final int addListenerElapsed = addListenerWatch.elapsedMicroseconds;
|
|
||||||
addListenerWatch.reset();
|
|
||||||
final int removeListenerElapsed = removeListenerWatch.elapsedMicroseconds;
|
|
||||||
removeListenerWatch.reset();
|
|
||||||
|
|
||||||
printer.addResult(
|
|
||||||
description: 'addListener ($listenersCount listeners)',
|
|
||||||
value: addListenerElapsed * _scale,
|
|
||||||
unit: 'ns per iteration',
|
|
||||||
name: 'addListener${listenersCount}_iteration',
|
|
||||||
);
|
|
||||||
|
|
||||||
printer.addResult(
|
|
||||||
description: 'removeListener ($listenersCount listeners)',
|
|
||||||
value: removeListenerElapsed * _scale,
|
|
||||||
unit: 'ns per iteration',
|
|
||||||
name: 'removeListener${listenersCount}_iteration',
|
|
||||||
);
|
|
||||||
|
|
||||||
printer.addResult(
|
|
||||||
description: 'notifyListener ($listenersCount listeners)',
|
|
||||||
value: notifyListener * _scale,
|
|
||||||
unit: 'ns per iteration',
|
|
||||||
name: 'notifyListener${listenersCount}_iteration',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runNotifyListenerBenchmark(int iteration, {bool addResult = true}) {
|
||||||
|
const String name = 'notifyListener';
|
||||||
|
|
||||||
|
for (int listenerCount = 0; listenerCount <= 5; listenerCount += 1) {
|
||||||
|
final _Notifier notifier = _Notifier();
|
||||||
|
for (int i = 1; i <= listenerCount; i += 1) {
|
||||||
|
notifier.addListener(() {});
|
||||||
|
}
|
||||||
|
final Stopwatch watch = Stopwatch();
|
||||||
|
watch.start();
|
||||||
|
for (int i = 0; i < iteration; i += 1) {
|
||||||
|
notifier.notify();
|
||||||
|
}
|
||||||
|
watch.stop();
|
||||||
|
final int elapsed = watch.elapsedMicroseconds;
|
||||||
|
final double averagePerIteration = elapsed / iteration;
|
||||||
|
if (addResult)
|
||||||
|
printer.addResult(
|
||||||
|
description: '$name ($listenerCount listeners)',
|
||||||
|
value: averagePerIteration * _kScale,
|
||||||
|
unit: 'ns per iteration',
|
||||||
|
name: '$name${listenerCount}_iteration',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void runRemoveListenerBenchmark(int iteration, {bool addResult = true}) {
|
||||||
|
const String name = 'removeListener';
|
||||||
|
final List<VoidCallback> listeners = <VoidCallback>[
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
];
|
||||||
|
for (int listenerCount = 1; listenerCount <= 5; listenerCount += 1) {
|
||||||
|
final List<_Notifier> notifiers = List<_Notifier>.generate(
|
||||||
|
iteration,
|
||||||
|
(_) {
|
||||||
|
final _Notifier notifier = _Notifier();
|
||||||
|
for (int l = 0; l < listenerCount; l += 1) {
|
||||||
|
notifier.addListener(listeners[l]);
|
||||||
|
}
|
||||||
|
return notifier;
|
||||||
|
},
|
||||||
|
growable: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Stopwatch watch = Stopwatch();
|
||||||
|
watch.start();
|
||||||
|
for (int i = 0; i < iteration; i += 1) {
|
||||||
|
for (int l = 0; l < listenerCount; l += 1) {
|
||||||
|
notifiers[i].removeListener(listeners[l]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch.stop();
|
||||||
|
final int elapsed = watch.elapsedMicroseconds;
|
||||||
|
final double averagePerIteration = elapsed / iteration;
|
||||||
|
if (addResult)
|
||||||
|
printer.addResult(
|
||||||
|
description: '$name ($listenerCount listeners)',
|
||||||
|
value: averagePerIteration * _kScale,
|
||||||
|
unit: 'ns per iteration',
|
||||||
|
name: '$name${listenerCount}_iteration',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void runRemoveListenerWhileNotifyingBenchmark(int iteration, {bool addResult = true}) {
|
||||||
|
const String name = 'removeListenerWhileNotifying';
|
||||||
|
|
||||||
|
final List<VoidCallback> listeners = <VoidCallback>[
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
() {},
|
||||||
|
];
|
||||||
|
for (int listenerCount = 1; listenerCount <= 5; listenerCount += 1) {
|
||||||
|
final List<_Notifier> notifiers = List<_Notifier>.generate(
|
||||||
|
iteration,
|
||||||
|
(_) {
|
||||||
|
final _Notifier notifier = _Notifier();
|
||||||
|
notifier.addListener(() {
|
||||||
|
// This listener will remove all other listeners. So that only this
|
||||||
|
// one is called and measured.
|
||||||
|
for (int l = 0; l < listenerCount; l += 1) {
|
||||||
|
notifier.removeListener(listeners[l]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (int l = 0; l < listenerCount; l += 1) {
|
||||||
|
notifier.addListener(listeners[l]);
|
||||||
|
}
|
||||||
|
return notifier;
|
||||||
|
},
|
||||||
|
growable: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Stopwatch watch = Stopwatch();
|
||||||
|
watch.start();
|
||||||
|
for (int i = 0; i < iteration; i += 1) {
|
||||||
|
notifiers[i].notify();
|
||||||
|
}
|
||||||
|
watch.stop();
|
||||||
|
final int elapsed = watch.elapsedMicroseconds;
|
||||||
|
final double averagePerIteration = elapsed / iteration;
|
||||||
|
if (addResult)
|
||||||
|
printer.addResult(
|
||||||
|
description: '$name ($listenerCount listeners)',
|
||||||
|
value: averagePerIteration * _kScale,
|
||||||
|
unit: 'ns per iteration',
|
||||||
|
name: '$name${listenerCount}_iteration',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runAddListenerBenchmark(_kNumWarmUp, addResult: false);
|
||||||
|
runAddListenerBenchmark(_kNumIterations, addResult: true);
|
||||||
|
|
||||||
|
runNotifyListenerBenchmark(_kNumWarmUp, addResult: false);
|
||||||
|
runNotifyListenerBenchmark(_kNumIterations, addResult: true);
|
||||||
|
|
||||||
|
runRemoveListenerBenchmark(_kNumWarmUp, addResult: false);
|
||||||
|
runRemoveListenerBenchmark(_kNumIterations, addResult: true);
|
||||||
|
|
||||||
|
runRemoveListenerWhileNotifyingBenchmark(_kNumWarmUp, addResult: false);
|
||||||
|
runRemoveListenerWhileNotifyingBenchmark(_kNumIterations, addResult: true);
|
||||||
|
|
||||||
printer.printToStdout();
|
printer.printToStdout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user