From b328cadaacb12fac16a1b173c48f53ac1ba1352d Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Mon, 27 Apr 2020 22:22:36 +0000 Subject: [PATCH] [benchmark] Benchmark for some .runtimeType patterns from Flutter Change-Id: I3799d6c8d7d35b9d293ecf0fb79c61cb10837356 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144813 Commit-Queue: Stephen Adams Reviewed-by: Jonas Termansen --- benchmarks/RuntimeType/dart/RuntimeType.dart | 176 +++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 benchmarks/RuntimeType/dart/RuntimeType.dart diff --git a/benchmarks/RuntimeType/dart/RuntimeType.dart b/benchmarks/RuntimeType/dart/RuntimeType.dart new file mode 100644 index 00000000000..fe21b30489c --- /dev/null +++ b/benchmarks/RuntimeType/dart/RuntimeType.dart @@ -0,0 +1,176 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Benchmark for runtimeType patterns as used in Flutter. + +// ignore_for_file: prefer_const_constructors +// ignore_for_file: avoid_function_literals_in_foreach_calls + +import 'dart:typed_data'; + +import 'package:benchmark_harness/benchmark_harness.dart'; + +abstract class Key { + const factory Key(String value) = ValueKey; + const Key.empty(); +} + +abstract class LocalKey extends Key { + const LocalKey() : super.empty(); +} + +class ValueKey extends LocalKey { + const ValueKey(this.value); + final T value; + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) return false; + return other is ValueKey && other.value == value; + } + + @override + int get hashCode => value.hashCode; +} + +abstract class Widget { + const Widget({this.key}); + final Key key; + + @pragma('dart2js:noInline') + static bool canUpdate(Widget oldWidget, Widget newWidget) { + return oldWidget.runtimeType == newWidget.runtimeType && + oldWidget.key == newWidget.key; + } +} + +class AWidget extends Widget { + const AWidget({Key key}) : super(key: key); +} + +class BWidget extends Widget { + const BWidget({Key key}) : super(key: key); +} + +class CWidget extends Widget { + const CWidget({Key key}) : super(key: key); +} + +class DWidget extends Widget { + const DWidget({Key key}) : super(key: key); +} + +class EWidget extends Widget { + const EWidget({Key key}) : super(key: key); +} + +class FWidget extends Widget { + const FWidget({Key key}) : super(key: key); +} + +class WWidget extends Widget { + final W /*?*/ ref; + const WWidget({this.ref, Key key}) : super(key: key); +} + +class WidgetCanUpdateBenchmark extends BenchmarkBase { + WidgetCanUpdateBenchmark() : super('RuntimeType.Widget.canUpdate.byType'); + + // All widgets have different types. + static List _widgets() => [ + AWidget(), + BWidget(), + CWidget(), + DWidget(), + EWidget(), + FWidget(), + WWidget(), + WWidget(ref: const BWidget()), + WWidget(ref: CWidget()), + const WWidget(ref: DWidget()), + ]; + // Bulk up list to reduce loop overheads. + final List widgets = _widgets() + _widgets() + _widgets(); + + @override + void exercise() => run(); + + @override + void run() { + for (var w1 in widgets) { + for (var w2 in widgets) { + if (Widget.canUpdate(w1, w2) != Widget.canUpdate(w2, w1)) { + throw 'Hmm $w1 $w2'; + } + } + } + } + + // Normalize by number of calls to [Widgets.canUpdate]. + @override + double measure() => super.measure() / (widgets.length * widgets.length * 2); +} + +class ValueKeyEqualBenchmark extends BenchmarkBase { + ValueKeyEqualBenchmark() : super('RuntimeType.Widget.canUpdate.byKey'); + + // All widgets the same class but distinguished on keys. + static List _widgets() => [ + AWidget(), + AWidget(key: ValueKey(1)), + AWidget(key: ValueKey(1)), + AWidget(key: ValueKey(2)), + AWidget(key: ValueKey(2)), + AWidget(key: ValueKey(3)), + AWidget(key: ValueKey('one')), + AWidget(key: ValueKey('two')), + AWidget(key: ValueKey('three')), + AWidget(key: ValueKey(Duration(seconds: 5))), + ]; + // Bulk up list to reduce loop overheads. + final List widgets = _widgets() + _widgets() + _widgets(); + + @override + void exercise() => run(); + + @override + void run() { + for (var w1 in widgets) { + for (var w2 in widgets) { + if (Widget.canUpdate(w1, w2) != Widget.canUpdate(w2, w1)) { + throw 'Hmm $w1 $w2'; + } + } + } + } + + // Normalize by number of calls to [Widgets.canUpdate]. + @override + double measure() => super.measure() / (widgets.length * widgets.length * 2); +} + +void pollute() { + // Various bits of code to make environment less unrealistic. + void check(dynamic a, dynamic b) { + if (a.runtimeType != b.runtimeType) throw 'mismatch $a $b'; + } + + check(Uint8List(1), Uint8List(2)); // dart2js needs native interceptors. + check(Int16List(1), Int16List(2)); + check([], []); + check({}, {}); +} + +void main() { + pollute(); + + final benchmarks = [ + WidgetCanUpdateBenchmark(), + ValueKeyEqualBenchmark(), + ]; + + // Warm up all benchmarks before running any. + benchmarks.forEach((bm) => bm.run()); + + benchmarks.forEach((bm) => bm.report()); +}