| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/foundation.dart'; |
| |
| import '../common.dart'; |
| |
| const int _kNumIterations = 65536; |
| const int _kNumWarmUp = 100; |
| const int _kScale = 1000; |
| |
| void main() { |
| assert(false, "Don't run benchmarks in debug mode! Use 'flutter run --release'."); |
| |
| // In the following benchmarks, we won't remove the listeners when we don't |
| // want to measure removeListener because we know that everything will be |
| // GC'ed in the end. |
| // Not removing listeners would cause memory leaks in a real application. |
| |
| final BenchmarkResultPrinter printer = BenchmarkResultPrinter(); |
| |
| void runAddListenerBenchmark(int iteration, {bool addResult = true}) { |
| const String name = 'add'; |
| for (int listenerCount = 1; listenerCount <= 5; listenerCount += 1) { |
| final List<_Notifier> notifiers = List<_Notifier>.generate( |
| iteration, |
| (_) => _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].addListener(() {}); |
| } |
| } |
| 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', |
| ); |
| } |
| } |
| } |
| |
| void runNotifyListenerBenchmark(int iteration, {bool addResult = true}) { |
| const String name = 'notify'; |
| |
| 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', |
| ); |
| } |
| } |
| } |
| |
| void runRemoveListenerBenchmark(int iteration, {bool addResult = true}) { |
| const String name = 'remove'; |
| 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', |
| ); |
| } |
| } |
| } |
| |
| void runRemoveListenerWhileNotifyingBenchmark(int iteration, |
| {bool addResult = true}) { |
| const String name = 'removeWhileNotify'; |
| |
| 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', |
| ); |
| } |
| } |
| } |
| |
| runAddListenerBenchmark(_kNumWarmUp, addResult: false); |
| runAddListenerBenchmark(_kNumIterations); |
| |
| runNotifyListenerBenchmark(_kNumWarmUp, addResult: false); |
| runNotifyListenerBenchmark(_kNumIterations); |
| |
| runRemoveListenerBenchmark(_kNumWarmUp, addResult: false); |
| runRemoveListenerBenchmark(_kNumIterations); |
| |
| runRemoveListenerWhileNotifyingBenchmark(_kNumWarmUp, addResult: false); |
| runRemoveListenerWhileNotifyingBenchmark(_kNumIterations); |
| |
| printer.printToStdout(); |
| } |
| |
| class _Notifier extends ChangeNotifier { |
| void notify() => notifyListeners(); |
| } |