mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:07:57 +00:00
[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 <sra@google.com> Reviewed-by: Jonas Termansen <sortie@google.com>
This commit is contained in:
parent
3e26a5d0dd
commit
b328cadaac
176
benchmarks/RuntimeType/dart/RuntimeType.dart
Normal file
176
benchmarks/RuntimeType/dart/RuntimeType.dart
Normal file
|
@ -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<String>;
|
||||
const Key.empty();
|
||||
}
|
||||
|
||||
abstract class LocalKey extends Key {
|
||||
const LocalKey() : super.empty();
|
||||
}
|
||||
|
||||
class ValueKey<T> extends LocalKey {
|
||||
const ValueKey(this.value);
|
||||
final T value;
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other.runtimeType != runtimeType) return false;
|
||||
return other is ValueKey<T> && 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<W extends Widget> 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<Widget> _widgets() => [
|
||||
AWidget(),
|
||||
BWidget(),
|
||||
CWidget(),
|
||||
DWidget(),
|
||||
EWidget(),
|
||||
FWidget(),
|
||||
WWidget<AWidget>(),
|
||||
WWidget<BWidget>(ref: const BWidget()),
|
||||
WWidget<CWidget>(ref: CWidget()),
|
||||
const WWidget<DWidget>(ref: DWidget()),
|
||||
];
|
||||
// Bulk up list to reduce loop overheads.
|
||||
final List<Widget> 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<Widget> _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<Widget> 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(<bool>{}, <bool>{});
|
||||
}
|
||||
|
||||
void main() {
|
||||
pollute();
|
||||
|
||||
final benchmarks = [
|
||||
WidgetCanUpdateBenchmark(),
|
||||
ValueKeyEqualBenchmark(),
|
||||
];
|
||||
|
||||
// Warm up all benchmarks before running any.
|
||||
benchmarks.forEach((bm) => bm.run());
|
||||
|
||||
benchmarks.forEach((bm) => bm.report());
|
||||
}
|
Loading…
Reference in a new issue