| // 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(); |
| } |
| } |