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