blob: 6b78476c446b2e8432be7434b99e9b358d0f55a5 [file] [log] [blame]
// 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();
}