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