mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:48:25 +00:00
af1b5b8044
We want dart2wasm be comparable to dart2js / dart2aot, the ladder two are much more conservative with inlining compared to current dart2wasm. The -O3 is described in the binaryen sources as agressive for performance and therefore willing to compromise code size. The -Os is more nuanced: It will perform many optimizations that are done in -O3 (and e.g. not in -O2) but it will make inlining less agressive. This reduces flute compile-time by 10% and code size by 10% This benchmark results are mixed (some things get faster, some things slower). Naturally there'll be specialized micro benchmarks that get hit hard by this. Where performance matters we should rather make dart2wasm use better inlining heuristics and annotate code with `@pragma('wasm:prefer-inline')` Change-Id: Idf7e75e4e385629c9cec66359efe0afe50db3e72 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352523 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
494 lines
12 KiB
Dart
494 lines
12 KiB
Dart
// Copyright (c) 2022, 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.
|
|
|
|
import 'package:benchmark_harness/benchmark_harness.dart';
|
|
|
|
// Micro-benchmark for List/Map/Set of records.
|
|
//
|
|
// The goal of this benchmark is to compare and track performance of
|
|
// the most common operations on List/Map/Set of records.
|
|
|
|
int N = 0;
|
|
int SUM = 0;
|
|
|
|
bool runtimeTrue = int.parse('1') == 1; // Not known at compile time.
|
|
|
|
class Pair {
|
|
final int v0;
|
|
final int v1;
|
|
const Pair(this.v0, this.v1);
|
|
@override
|
|
bool operator ==(other) => other is Pair && v0 == other.v0 && v1 == other.v1;
|
|
@override
|
|
int get hashCode => Object.hash(v0, v1);
|
|
}
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
List<Object> getPolymorphicListOfClass(
|
|
int length, bool growable, bool withValues) {
|
|
if (runtimeTrue) {
|
|
if (withValues) {
|
|
return List<Pair>.generate(length, (i) => Pair(i, i), growable: growable);
|
|
} else {
|
|
return List<Pair>.filled(length, const Pair(-1, -1), growable: growable);
|
|
}
|
|
} else {
|
|
return List<String>.filled(0, '', growable: growable);
|
|
}
|
|
}
|
|
|
|
@pragma('vm:never-inline')
|
|
@pragma('wasm:never-inline')
|
|
@pragma('dart2js:never-inline')
|
|
List<Object> getPolymorphicListOfRecords(
|
|
int length, bool growable, bool withValues) {
|
|
if (runtimeTrue) {
|
|
if (withValues) {
|
|
return List<(int, int)>.generate(length, (i) => (i, i),
|
|
growable: growable);
|
|
} else {
|
|
return List<(int, int)>.filled(length, (-1, -1), growable: growable);
|
|
}
|
|
} else {
|
|
return List<String>.filled(0, '', growable: growable);
|
|
}
|
|
}
|
|
|
|
class BenchListAddClass extends BenchmarkBase {
|
|
BenchListAddClass() : super('RecordCollections.ListAdd.Class');
|
|
|
|
@override
|
|
void run() {
|
|
final list = <Pair>[];
|
|
for (int i = 0; i < N; ++i) {
|
|
list.add(Pair(i, i));
|
|
}
|
|
if (list[N ~/ 2].v0 != N ~/ 2) throw 'Bad result: ${list[N ~/ 2].v0}';
|
|
}
|
|
}
|
|
|
|
class BenchListAddRecord extends BenchmarkBase {
|
|
BenchListAddRecord() : super('RecordCollections.ListAdd.Record');
|
|
|
|
@override
|
|
void run() {
|
|
final list = <(int, int)>[];
|
|
for (int i = 0; i < N; ++i) {
|
|
list.add((i, i));
|
|
}
|
|
if (list[N ~/ 2].$1 != N ~/ 2) throw 'Bad result: ${list[N ~/ 2].$1}';
|
|
}
|
|
}
|
|
|
|
class BenchListAddPolyClass extends BenchmarkBase {
|
|
BenchListAddPolyClass() : super('RecordCollections.ListAddPoly.Class');
|
|
|
|
@override
|
|
void run() {
|
|
final List<Object> list = getPolymorphicListOfClass(0, runtimeTrue, false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list.add(Pair(i, i));
|
|
}
|
|
final int mid = (list[N ~/ 2] as Pair).v0;
|
|
if (mid != N ~/ 2) throw 'Bad result: $mid';
|
|
}
|
|
}
|
|
|
|
class BenchListAddPolyRecord extends BenchmarkBase {
|
|
BenchListAddPolyRecord() : super('RecordCollections.ListAddPoly.Record');
|
|
|
|
@override
|
|
void run() {
|
|
final List<Object> list =
|
|
getPolymorphicListOfRecords(0, runtimeTrue, false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list.add((i, i));
|
|
}
|
|
final int mid = (list[N ~/ 2] as (int, int)).$1;
|
|
if (mid != N ~/ 2) throw 'Bad result: $mid';
|
|
}
|
|
}
|
|
|
|
class BenchListSetIndexedClass extends BenchmarkBase {
|
|
BenchListSetIndexedClass() : super('RecordCollections.ListSetIndexed.Class');
|
|
|
|
@override
|
|
void run() {
|
|
final list = List<Pair>.filled(N, const Pair(-1, -1), growable: false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list[i] = Pair(i, i);
|
|
}
|
|
if (list[N ~/ 2].v0 != N ~/ 2) throw 'Bad result: ${list[N ~/ 2].v0}';
|
|
}
|
|
}
|
|
|
|
class BenchListSetIndexedRecord extends BenchmarkBase {
|
|
BenchListSetIndexedRecord()
|
|
: super('RecordCollections.ListSetIndexed.Record');
|
|
|
|
@override
|
|
void run() {
|
|
final list = List<(int, int)>.filled(N, (-1, -1), growable: false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list[i] = (i, i);
|
|
}
|
|
if (list[N ~/ 2].$1 != N ~/ 2) throw 'Bad result: ${list[N ~/ 2].$1}';
|
|
}
|
|
}
|
|
|
|
class BenchListSetIndexedPolyClass extends BenchmarkBase {
|
|
BenchListSetIndexedPolyClass()
|
|
: super('RecordCollections.ListSetIndexedPoly.Class');
|
|
|
|
@override
|
|
void run() {
|
|
final List<Object> list = getPolymorphicListOfClass(N, !runtimeTrue, false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list[i] = Pair(i, i);
|
|
}
|
|
final int mid = (list[N ~/ 2] as Pair).v0;
|
|
if (mid != N ~/ 2) throw 'Bad result: $mid';
|
|
}
|
|
}
|
|
|
|
class BenchListSetIndexedPolyRecord extends BenchmarkBase {
|
|
BenchListSetIndexedPolyRecord()
|
|
: super('RecordCollections.ListSetIndexedPoly.Record');
|
|
|
|
@override
|
|
void run() {
|
|
final List<Object> list =
|
|
getPolymorphicListOfRecords(N, !runtimeTrue, false);
|
|
for (int i = 0; i < N; ++i) {
|
|
list[i] = (i, i);
|
|
}
|
|
final int mid = (list[N ~/ 2] as (int, int)).$1;
|
|
if (mid != N ~/ 2) throw 'Bad result: $mid';
|
|
}
|
|
}
|
|
|
|
class BenchListGetIndexedClass extends BenchmarkBase {
|
|
BenchListGetIndexedClass() : super('RecordCollections.ListGetIndexed.Class');
|
|
|
|
final list = <Pair>[
|
|
for (int i = 0; i < N; ++i) Pair(i, i),
|
|
];
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < list.length; ++i) {
|
|
sum += list[i].v0;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListGetIndexedRecord extends BenchmarkBase {
|
|
BenchListGetIndexedRecord()
|
|
: super('RecordCollections.ListGetIndexed.Record');
|
|
|
|
final list = <(int, int)>[
|
|
for (int i = 0; i < N; ++i) (i, i),
|
|
];
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < list.length; ++i) {
|
|
sum += list[i].$1;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListGetIndexedPolyClass extends BenchmarkBase {
|
|
BenchListGetIndexedPolyClass()
|
|
: super('RecordCollections.ListGetIndexedPoly.Class');
|
|
|
|
final list = getPolymorphicListOfClass(N, runtimeTrue, true) as List<Pair>;
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < list.length; ++i) {
|
|
sum += list[i].v0;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListGetIndexedPolyRecord extends BenchmarkBase {
|
|
BenchListGetIndexedPolyRecord()
|
|
: super('RecordCollections.ListGetIndexedPoly.Record');
|
|
|
|
final list =
|
|
getPolymorphicListOfRecords(N, runtimeTrue, true) as List<(int, int)>;
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < list.length; ++i) {
|
|
sum += list[i].$1;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListIterateClass extends BenchmarkBase {
|
|
BenchListIterateClass() : super('RecordCollections.ListIterate.Class');
|
|
|
|
final list = <Pair>[
|
|
for (int i = 0; i < N; ++i) Pair(i, i),
|
|
];
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (final v in list) {
|
|
sum += v.v0;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListIterateRecord extends BenchmarkBase {
|
|
BenchListIterateRecord() : super('RecordCollections.ListIterate.Record');
|
|
|
|
final list = <(int, int)>[
|
|
for (int i = 0; i < N; ++i) (i, i),
|
|
];
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (final v in list) {
|
|
sum += v.$1;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListIteratePolyClass extends BenchmarkBase {
|
|
BenchListIteratePolyClass()
|
|
: super('RecordCollections.ListIteratePoly.Class');
|
|
|
|
final list = getPolymorphicListOfClass(N, runtimeTrue, true) as List<Pair>;
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (final v in list) {
|
|
sum += v.v0;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchListIteratePolyRecord extends BenchmarkBase {
|
|
BenchListIteratePolyRecord()
|
|
: super('RecordCollections.ListIteratePoly.Record');
|
|
|
|
final list =
|
|
getPolymorphicListOfRecords(N, runtimeTrue, true) as List<(int, int)>;
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (final v in list) {
|
|
sum += v.$1;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchMapAddClassKey extends BenchmarkBase {
|
|
BenchMapAddClassKey() : super('RecordCollections.MapAdd.ClassKey');
|
|
|
|
@override
|
|
void run() {
|
|
final map = <Pair, int>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
map[Pair(i, i)] = i;
|
|
}
|
|
if (map.length != N) throw 'Bad result: ${map.length}';
|
|
}
|
|
}
|
|
|
|
class BenchMapAddRecordKey extends BenchmarkBase {
|
|
BenchMapAddRecordKey() : super('RecordCollections.MapAdd.RecordKey');
|
|
|
|
@override
|
|
void run() {
|
|
final map = <(int, int), int>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
map[(i, i)] = i;
|
|
}
|
|
if (map.length != N) throw 'Bad result: ${map.length}';
|
|
}
|
|
}
|
|
|
|
class BenchMapAddClassValue extends BenchmarkBase {
|
|
BenchMapAddClassValue() : super('RecordCollections.MapAdd.ClassValue');
|
|
|
|
@override
|
|
void run() {
|
|
final map = <int, Pair>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
map[i] = Pair(i, i);
|
|
}
|
|
if (map.length != N) throw 'Bad result: ${map.length}';
|
|
}
|
|
}
|
|
|
|
class BenchMapAddRecordValue extends BenchmarkBase {
|
|
BenchMapAddRecordValue() : super('RecordCollections.MapAdd.RecordValue');
|
|
|
|
@override
|
|
void run() {
|
|
final map = <int, (int, int)>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
map[i] = (i, i);
|
|
}
|
|
if (map.length != N) throw 'Bad result: ${map.length}';
|
|
}
|
|
}
|
|
|
|
class BenchMapLookupClass extends BenchmarkBase {
|
|
BenchMapLookupClass() : super('RecordCollections.MapLookup.Class');
|
|
|
|
final map = <Pair, int>{
|
|
for (int i = 0; i < N; ++i) Pair(i, i): i,
|
|
};
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
sum += map[Pair(i, i)]!;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchMapLookupRecord extends BenchmarkBase {
|
|
BenchMapLookupRecord() : super('RecordCollections.MapLookup.Record');
|
|
|
|
final map = <(int, int), int>{
|
|
for (int i = 0; i < N; ++i) (i, i): i,
|
|
};
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
sum += map[(i, i)]!;
|
|
}
|
|
if (sum != SUM) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchSetAddClass extends BenchmarkBase {
|
|
BenchSetAddClass() : super('RecordCollections.SetAdd.Class');
|
|
|
|
@override
|
|
void run() {
|
|
final set = <Pair>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
set.add(Pair(i, i));
|
|
}
|
|
if (set.length != N) throw 'Bad result: ${set.length}';
|
|
}
|
|
}
|
|
|
|
class BenchSetAddRecord extends BenchmarkBase {
|
|
BenchSetAddRecord() : super('RecordCollections.SetAdd.Record');
|
|
|
|
@override
|
|
void run() {
|
|
final set = <(int, int)>{};
|
|
for (int i = 0; i < N; ++i) {
|
|
set.add((i, i));
|
|
}
|
|
if (set.length != N) throw 'Bad result: ${set.length}';
|
|
}
|
|
}
|
|
|
|
class BenchSetLookupClass extends BenchmarkBase {
|
|
BenchSetLookupClass() : super('RecordCollections.SetLookup.Class');
|
|
|
|
final set = <Pair>{
|
|
for (int i = 0; i < N ~/ 2; ++i) Pair(i * 2, i * 2),
|
|
};
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
sum += set.contains(Pair(i, i)) ? 1 : 0;
|
|
}
|
|
if (sum != N ~/ 2) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
class BenchSetLookupRecord extends BenchmarkBase {
|
|
BenchSetLookupRecord() : super('RecordCollections.SetLookup.Record');
|
|
|
|
final set = <(int, int)>{
|
|
for (int i = 0; i < N ~/ 2; ++i) (i * 2, i * 2),
|
|
};
|
|
|
|
@override
|
|
void run() {
|
|
int sum = 0;
|
|
for (int i = 0; i < N; ++i) {
|
|
sum += set.contains((i, i)) ? 1 : 0;
|
|
}
|
|
if (sum != N ~/ 2) throw 'Bad result: $sum';
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
N = int.parse('100000');
|
|
SUM = N * (N - 1) ~/ 2;
|
|
|
|
final benchmarks = [
|
|
BenchListAddClass(),
|
|
BenchListAddRecord(),
|
|
BenchListAddPolyClass(),
|
|
BenchListAddPolyRecord(),
|
|
BenchListSetIndexedClass(),
|
|
BenchListSetIndexedRecord(),
|
|
BenchListSetIndexedPolyClass(),
|
|
BenchListSetIndexedPolyRecord(),
|
|
BenchListGetIndexedClass(),
|
|
BenchListGetIndexedRecord(),
|
|
BenchListGetIndexedPolyClass(),
|
|
BenchListGetIndexedPolyRecord(),
|
|
BenchListIterateClass(),
|
|
BenchListIterateRecord(),
|
|
BenchListIteratePolyClass(),
|
|
BenchListIteratePolyRecord(),
|
|
BenchMapAddClassKey(),
|
|
BenchMapAddRecordKey(),
|
|
BenchMapAddClassValue(),
|
|
BenchMapAddRecordValue(),
|
|
BenchMapLookupClass(),
|
|
BenchMapLookupRecord(),
|
|
BenchSetAddClass(),
|
|
BenchSetAddRecord(),
|
|
BenchSetLookupClass(),
|
|
BenchSetLookupRecord(),
|
|
];
|
|
|
|
for (final benchmark in benchmarks) {
|
|
benchmark.warmup();
|
|
}
|
|
for (final benchmark in benchmarks) {
|
|
benchmark.report();
|
|
}
|
|
}
|