| // 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; |
| |
| Future<void> execute() async { |
| 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 printer = BenchmarkResultPrinter(); |
| |
| void runAddListenerBenchmark(int iteration, {bool addResult = true}) { |
| const name = 'add'; |
| for (var listenerCount = 1; listenerCount <= 5; listenerCount += 1) { |
| final notifiers = List<_Notifier>.generate(iteration, (_) => _Notifier(), growable: false); |
| |
| final watch = Stopwatch(); |
| watch.start(); |
| for (var i = 0; i < iteration; i += 1) { |
| for (var 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 name = 'notify'; |
| |
| for (var listenerCount = 0; listenerCount <= 5; listenerCount += 1) { |
| final notifier = _Notifier(); |
| for (var i = 1; i <= listenerCount; i += 1) { |
| notifier.addListener(() {}); |
| } |
| final watch = Stopwatch(); |
| watch.start(); |
| for (var 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 name = 'remove'; |
| final listeners = <VoidCallback>[() {}, () {}, () {}, () {}, () {}]; |
| for (var listenerCount = 1; listenerCount <= 5; listenerCount += 1) { |
| final notifiers = List<_Notifier>.generate(iteration, (_) { |
| final notifier = _Notifier(); |
| for (var l = 0; l < listenerCount; l += 1) { |
| notifier.addListener(listeners[l]); |
| } |
| return notifier; |
| }, growable: false); |
| |
| final watch = Stopwatch(); |
| watch.start(); |
| for (var i = 0; i < iteration; i += 1) { |
| for (var 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 name = 'removeWhileNotify'; |
| |
| final listeners = <VoidCallback>[() {}, () {}, () {}, () {}, () {}]; |
| for (var listenerCount = 1; listenerCount <= 5; listenerCount += 1) { |
| final notifiers = List<_Notifier>.generate(iteration, (_) { |
| final notifier = _Notifier(); |
| notifier.addListener(() { |
| // This listener will remove all other listeners. So that only this |
| // one is called and measured. |
| for (var l = 0; l < listenerCount; l += 1) { |
| notifier.removeListener(listeners[l]); |
| } |
| }); |
| for (var l = 0; l < listenerCount; l += 1) { |
| notifier.addListener(listeners[l]); |
| } |
| return notifier; |
| }, growable: false); |
| |
| final watch = Stopwatch(); |
| watch.start(); |
| for (var 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(); |
| } |