change_notifier_bench.dart 5.8 KB
Newer Older
1 2 3 4 5 6 7 8
// 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';

9
const int _kNumIterations = 65536;
10
const int _kNumWarmUp = 100;
11
const int _kScale = 1000;
12 13

void main() {
14
  assert(false, "Don't run benchmarks in debug mode! Use 'flutter run --release'.");
15

16 17 18 19
  // 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.
20 21 22

  final BenchmarkResultPrinter printer = BenchmarkResultPrinter();

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
  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;
42
      if (addResult) {
43 44 45 46 47 48
        printer.addResult(
          description: '$name ($listenerCount listeners)',
          value: averagePerIteration * _kScale,
          unit: 'ns per iteration',
          name: '$name$listenerCount',
        );
49
      }
50 51 52 53 54
    }
  }

  void runNotifyListenerBenchmark(int iteration, {bool addResult = true}) {
    const String name = 'notify';
55

56
    for (int listenerCount = 0; listenerCount <= 5; listenerCount += 1) {
57
      final _Notifier notifier = _Notifier();
58 59 60 61 62 63 64 65 66 67 68
      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;
69
      if (addResult) {
70 71 72 73 74 75
        printer.addResult(
          description: '$name ($listenerCount listeners)',
          value: averagePerIteration * _kScale,
          unit: 'ns per iteration',
          name: '$name$listenerCount',
        );
76
      }
77
    }
78
  }
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
  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;
112
      if (addResult) {
113 114 115 116 117 118
        printer.addResult(
          description: '$name ($listenerCount listeners)',
          value: averagePerIteration * _kScale,
          unit: 'ns per iteration',
          name: '$name$listenerCount',
        );
119
      }
120
    }
121 122
  }

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  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;
162
      if (addResult) {
163 164 165 166 167 168
        printer.addResult(
          description: '$name ($listenerCount listeners)',
          value: averagePerIteration * _kScale,
          unit: 'ns per iteration',
          name: '$name$listenerCount',
        );
169
      }
170 171 172 173
    }
  }

  runAddListenerBenchmark(_kNumWarmUp, addResult: false);
174
  runAddListenerBenchmark(_kNumIterations);
175 176

  runNotifyListenerBenchmark(_kNumWarmUp, addResult: false);
177
  runNotifyListenerBenchmark(_kNumIterations);
178 179

  runRemoveListenerBenchmark(_kNumWarmUp, addResult: false);
180
  runRemoveListenerBenchmark(_kNumIterations);
181 182

  runRemoveListenerWhileNotifyingBenchmark(_kNumWarmUp, addResult: false);
183
  runRemoveListenerWhileNotifyingBenchmark(_kNumIterations);
184

185 186 187 188 189 190
  printer.printToStdout();
}

class _Notifier extends ChangeNotifier {
  void notify() => notifyListeners();
}