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