From 4c4adc14bec2b7d522ccb7b073efe99a47e0741a Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 13 Dec 2024 07:51:25 -0800 Subject: [PATCH] separate out benchmark into 4 stages: create, serialize, deserialize, hash (#154) As mentioned in https://github.com/dart-lang/macros/pull/153. This gives us a better overall picture of where time is going, in the various modes. Current results: ``` SdkMapsJsonWireBenchmark-create: 904ms SdkMapsBufferWireBenchmark-create: 711ms SdkMapsBuilderWireBenchmark-create: 713ms LazyMapsJsonWireBenchmark-create: 0ms LazyMapsBufferWireBenchmark-create: 0ms LazyWrappersBufferWireBenchmark-create: 420ms BuilderMapsJsonWireBenchmark-create: 374ms BuilderMapsBuilderWireBenchmark-create: 379ms SdkMapsJsonWireBenchmark-serialize: 746ms, 7177227 bytes SdkMapsBufferWireBenchmark-serialize: 633ms, 5122684 bytes SdkMapsBuilderWireBenchmark-serialize: 667ms, 4859868 bytes LazyMapsJsonWireBenchmark-serialize: 1104ms, 7177227 bytes LazyMapsBufferWireBenchmark-serialize: 543ms, 5122684 bytes LazyWrappersBufferWireBenchmark-serialize: 0ms, 2111761 bytes BuilderMapsJsonWireBenchmark-serialize: 1028ms, 7177227 bytes BuilderMapsBuilderWireBenchmark-serialize: 0ms, 2111761 bytes SdkMapsJsonWireBenchmark-deserialize: 1249ms SdkMapsBufferWireBenchmark-deserialize: 0ms SdkMapsBuilderWireBenchmark-deserialize: 0ms LazyMapsJsonWireBenchmark-deserialize: 1298ms LazyMapsBufferWireBenchmark-deserialize: 0ms LazyWrappersBufferWireBenchmark-deserialize: 0ms BuilderMapsJsonWireBenchmark-deserialize: 1257ms BuilderMapsBuilderWireBenchmark-deserialize: 0ms SdkMapsJsonWireBenchmark-process: 151ms, hash 23186292 SdkMapsBufferWireBenchmark-process: 249ms, hash 23186292 SdkMapsBuilderWireBenchmark-process: 541ms, hash 23186292 LazyMapsJsonWireBenchmark-process: 179ms, hash 23186292 LazyMapsBufferWireBenchmark-process: 251ms, hash 23186292 LazyWrappersBufferWireBenchmark-process: 367ms, hash 23186292 BuilderMapsJsonWireBenchmark-process: 207ms, hash 23186292 BuilderMapsBuilderWireBenchmark-process: 364ms, hash 23186292 SdkMapsJsonWireBenchmark-total: 3050ms SdkMapsBufferWireBenchmark-total: 1593ms SdkMapsBuilderWireBenchmark-total: 1921ms LazyMapsJsonWireBenchmark-total: 2581ms LazyMapsBufferWireBenchmark-total: 794ms LazyWrappersBufferWireBenchmark-total: 787ms BuilderMapsJsonWireBenchmark-total: 2866ms BuilderMapsBuilderWireBenchmark-total: 743ms ``` --- .../builder_maps_builder_wire_benchmark.dart | 15 +-- .../builder_maps_json_wire_benchmark.dart | 10 +- .../lazy_maps_buffer_wire_benchmark.dart | 13 ++- .../lazy_maps_json_wire_benchmark.dart | 10 +- .../lazy_wrappers_buffer_wire_benchmark.dart | 22 ++-- pkgs/dart_model/benchmark/main.dart | 85 +++++++++----- .../benchmark/regular_dart_classes.dart | 31 +++-- .../sdk_maps_buffer_wire_benchmark.dart | 12 +- .../sdk_maps_builder_wire_benchmark.dart | 13 ++- .../sdk_maps_json_wire_benchmark.dart | 10 +- .../benchmark/serialization_benchmark.dart | 108 +++++++++++------- 11 files changed, 188 insertions(+), 141 deletions(-) diff --git a/pkgs/dart_model/benchmark/builder_maps_builder_wire_benchmark.dart b/pkgs/dart_model/benchmark/builder_maps_builder_wire_benchmark.dart index 6b0fba05..db126de6 100644 --- a/pkgs/dart_model/benchmark/builder_maps_builder_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/builder_maps_builder_wire_benchmark.dart @@ -2,6 +2,8 @@ // 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 'dart:typed_data'; + import 'package:dart_model/src/json_buffer/json_buffer_builder.dart'; import 'serialization_benchmark.dart'; @@ -11,12 +13,12 @@ JsonBufferBuilder? runningBuffer; /// Benchmark accumulating data directly into a [JsonBufferBuilder]. class BuilderMapsBuilderWireBenchmark extends SerializationBenchmark { @override - void run() { - createData(); - - serialized = runningBuffer!.serialize(); + Uint8List serialize(Map data) { + assert(data == runningBuffer!.map); + return runningBuffer!.serialize(); } + @override Map createData() { final buffer = runningBuffer = JsonBufferBuilder(); final map = buffer.map; @@ -44,9 +46,8 @@ class BuilderMapsBuilderWireBenchmark extends SerializationBenchmark { } @override - void deserialize() { - deserialized = JsonBufferBuilder.deserialize(serialized!).map; - } + Map deserialize(Uint8List serialized) => + JsonBufferBuilder.deserialize(serialized).map; Member _makeMember(String key) { final intKey = key.length; diff --git a/pkgs/dart_model/benchmark/builder_maps_json_wire_benchmark.dart b/pkgs/dart_model/benchmark/builder_maps_json_wire_benchmark.dart index 4d48c1a8..7e78e124 100644 --- a/pkgs/dart_model/benchmark/builder_maps_json_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/builder_maps_json_wire_benchmark.dart @@ -11,12 +11,10 @@ import 'builder_maps_builder_wire_benchmark.dart'; /// serializing it to JSON. class BuilderMapsJsonWireBenchmark extends BuilderMapsBuilderWireBenchmark { @override - void run() { - serialized = json.fuse(utf8).encode(createData()) as Uint8List; - } + Uint8List serialize(Map data) => + json.fuse(utf8).encode(data) as Uint8List; @override - void deserialize() { - deserialized = json.fuse(utf8).decode(serialized!) as Map; - } + Map deserialize(Uint8List serialized) => + json.fuse(utf8).decode(serialized) as Map; } diff --git a/pkgs/dart_model/benchmark/lazy_maps_buffer_wire_benchmark.dart b/pkgs/dart_model/benchmark/lazy_maps_buffer_wire_benchmark.dart index 34259b5b..a800e3f1 100644 --- a/pkgs/dart_model/benchmark/lazy_maps_buffer_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/lazy_maps_buffer_wire_benchmark.dart @@ -2,16 +2,18 @@ // 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 'dart:typed_data'; + import 'json_buffer.dart'; import 'serialization_benchmark.dart'; /// Benchmark accumulating data into a [JsonBuffer] via [LazyMap]. class LazyMapsBufferWireBenchmark extends SerializationBenchmark { @override - void run() { - serialized = JsonBuffer(createData()).serialize(); - } + Uint8List serialize(Map data) => + JsonBuffer(data).serialize(); + @override LazyMap createData() { return LazyMap(mapKeys, (key) { final intKey = int.parse(key); @@ -44,9 +46,8 @@ class LazyMapsBufferWireBenchmark extends SerializationBenchmark { } @override - void deserialize() { - deserialized = JsonBuffer.deserialize(serialized!).asMap; - } + Map deserialize(Uint8List serialized) => + JsonBuffer.deserialize(serialized).asMap; } /// An interface. diff --git a/pkgs/dart_model/benchmark/lazy_maps_json_wire_benchmark.dart b/pkgs/dart_model/benchmark/lazy_maps_json_wire_benchmark.dart index 632aba8f..4f4ec051 100644 --- a/pkgs/dart_model/benchmark/lazy_maps_json_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/lazy_maps_json_wire_benchmark.dart @@ -11,12 +11,10 @@ import 'lazy_maps_buffer_wire_benchmark.dart'; /// serializing it to JSON. class LazyMapsJsonWireBenchmark extends LazyMapsBufferWireBenchmark { @override - void run() { - serialized = json.fuse(utf8).encode(createData()) as Uint8List; - } + Uint8List serialize(Map data) => + json.fuse(utf8).encode(data) as Uint8List; @override - void deserialize() { - deserialized = json.fuse(utf8).decode(serialized!) as Map; - } + Map deserialize(Uint8List serialized) => + json.fuse(utf8).decode(serialized) as Map; } diff --git a/pkgs/dart_model/benchmark/lazy_wrappers_buffer_wire_benchmark.dart b/pkgs/dart_model/benchmark/lazy_wrappers_buffer_wire_benchmark.dart index d92a3e62..f34c5786 100644 --- a/pkgs/dart_model/benchmark/lazy_wrappers_buffer_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/lazy_wrappers_buffer_wire_benchmark.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:collection'; +import 'dart:typed_data'; import 'package:dart_model/src/json_buffer/json_buffer_builder.dart'; @@ -15,12 +16,12 @@ JsonBufferBuilder? runningBuffer; /// extension type). class LazyWrappersBufferWireBenchmark extends SerializationBenchmark { @override - void run() { - createData(); - - serialized = runningBuffer!.serialize(); + Uint8List serialize(Map data) { + assert(data == runningBuffer!.map); + return runningBuffer!.serialize(); } + @override Map createData() { final buffer = runningBuffer = JsonBufferBuilder(); final map = buffer.map; @@ -62,13 +63,12 @@ class LazyWrappersBufferWireBenchmark extends SerializationBenchmark { } @override - void deserialize() { - deserialized = _LazyMap( - JsonBufferBuilder.deserialize(serialized!).map, - (json) => Interface.fromJson(json as Map), - (i) => i.toJson(), - ); - } + Map deserialize(Uint8List serialized) => + _LazyMap( + JsonBufferBuilder.deserialize(serialized).map, + (json) => Interface.fromJson(json as Map), + (i) => i.toJson(), + ); } class _LazyMap extends MapBase { diff --git a/pkgs/dart_model/benchmark/main.dart b/pkgs/dart_model/benchmark/main.dart index c7d06154..7818da49 100644 --- a/pkgs/dart_model/benchmark/main.dart +++ b/pkgs/dart_model/benchmark/main.dart @@ -2,6 +2,8 @@ // 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 'dart:math'; + import 'package:collection/collection.dart'; import 'builder_maps_builder_wire_benchmark.dart'; @@ -15,51 +17,76 @@ import 'regular_dart_classes.dart' as regular; import 'sdk_maps_buffer_wire_benchmark.dart'; import 'sdk_maps_builder_wire_benchmark.dart'; import 'sdk_maps_json_wire_benchmark.dart'; +import 'serialization_benchmark.dart'; void main() { - final sdkMapsJsonWireBenchmark = SdkMapsJsonWireBenchmark(); - final lazyMapsBufferWireBenchmark = LazyMapsBufferWireBenchmark(); - final lazyWrappersBufferWireBenchmark = LazyWrappersBufferWireBenchmark(); - final builderMapsBuilderWireBenchmark = BuilderMapsBuilderWireBenchmark(); - final regularClassesBufferWireBenchmark = RegularClassesBufferWireBenchmark(); final serializationBenchmarks = [ - sdkMapsJsonWireBenchmark, + SdkMapsJsonWireBenchmark(), SdkMapsBufferWireBenchmark(), SdkMapsBuilderWireBenchmark(), LazyMapsJsonWireBenchmark(), - lazyMapsBufferWireBenchmark, - lazyWrappersBufferWireBenchmark, + LazyMapsBufferWireBenchmark(), + LazyWrappersBufferWireBenchmark(), BuilderMapsJsonWireBenchmark(), - builderMapsBuilderWireBenchmark, - regularClassesBufferWireBenchmark, + BuilderMapsBuilderWireBenchmark(), + RegularClassesBufferWireBenchmark(), ]; for (var i = 0; i != 3; ++i) { - for (final benchmark in serializationBenchmarks) { - final measure = benchmark.measure().toMilliseconds; - final paddedName = benchmark.name.padLeft(31); - final paddedMeasure = '${measure}ms'.padLeft(6); + // Collects the total measurements from all phases, per benchmark. + final totals = { + for (var benchmark in serializationBenchmarks) benchmark: 0, + }; + + for (var stage in BenchmarkStage.values) { + var padding = 0; + for (final benchmark in serializationBenchmarks) { + benchmark.stage = stage; + padding = max(padding, benchmark.name.length); + } - final paddedBytes = '${benchmark.serialized!.length}'.padLeft(7); - print('$paddedName: $paddedMeasure, $paddedBytes bytes'); - benchmark.deserialize(); + for (final benchmark in serializationBenchmarks) { + final measure = benchmark.measure().toMilliseconds; + totals[benchmark] = totals[benchmark]! + measure; + + var buffer = + StringBuffer(benchmark.name.padLeft(padding + 1)) + ..write(': ') + ..write('${measure}ms'.padLeft(6)); + + switch (stage) { + case BenchmarkStage.serialize: + final paddedBytes = '${benchmark.bytes}'.padLeft(7); + buffer.write(', $paddedBytes bytes'); + case BenchmarkStage.process: + buffer.write(', hash ${benchmark.hashResult}'); + default: + } + print(buffer.toString()); + } } - print(''); - for (final benchmark in [ - sdkMapsJsonWireBenchmark.processBenchmark(), - lazyMapsBufferWireBenchmark.processBenchmark(), - lazyWrappersBufferWireBenchmark.processBenchmark(), - builderMapsBuilderWireBenchmark.processBenchmark(), - regularClassesBufferWireBenchmark.processBenchmark(), - ]) { - final measure = benchmark.measure().toMilliseconds; - final paddedName = benchmark.name.padLeft(36); - final paddedMeasure = '${measure}ms'.padLeft(6); + // Add up the totals and print them. + { + var padding = 0; + String name(SerializationBenchmark benchmark) => + '${benchmark.runtimeType}-total'; - print('$paddedName: $paddedMeasure, hash ${benchmark.computedResult}'); + for (final benchmark in serializationBenchmarks) { + padding = max(padding, name(benchmark).length); + } + for (var benchmark in serializationBenchmarks) { + var buffer = + StringBuffer(name(benchmark).padLeft(padding + 1)) + ..write(':') + ..write('${totals[benchmark]}ms'.padLeft(7)); + print(buffer.toString()); + } } + print(''); + + print('validating benchmark results (this is slow)'); for (final benchmark in serializationBenchmarks.skip(1)) { var deserialized = benchmark.deserialized; // Need to unwrap these to compare them as raw maps. diff --git a/pkgs/dart_model/benchmark/regular_dart_classes.dart b/pkgs/dart_model/benchmark/regular_dart_classes.dart index 80f56925..a352d79f 100644 --- a/pkgs/dart_model/benchmark/regular_dart_classes.dart +++ b/pkgs/dart_model/benchmark/regular_dart_classes.dart @@ -2,25 +2,18 @@ // 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 'dart:typed_data'; + import 'package:dart_model/src/json_buffer/json_buffer_builder.dart'; import 'serialization_benchmark.dart'; JsonBufferBuilder? runningBuffer; -/// Benchmark accumulating data directly into a [JsonBufferBuilder] with an -/// indirection through a thin wrapper type (which is a real type, not an -/// extension type). +/// Benchmark for regular dart classes which serialize and deserializ into +/// a [JsonBufferBuilder]. class RegularClassesBufferWireBenchmark extends SerializationBenchmark { @override - void run() { - var data = createData(); - - final buffer = runningBuffer = JsonBufferBuilder(); - data.forEach((k, v) => buffer.map[k] = v.toJson()); - serialized = runningBuffer!.serialize(); - } - /// Creates the data, but its not ready yet to be serialized. Map createData() { final map = {}; @@ -62,12 +55,16 @@ class RegularClassesBufferWireBenchmark extends SerializationBenchmark { } @override - void deserialize() { - deserialized = JsonBufferBuilder.deserialize( - serialized!, - ).map.map( - (k, v) => MapEntry(k, Interface.fromJson(v as Map)), - ); + Map deserialize(Uint8List serialized) => + JsonBufferBuilder.deserialize(serialized).map.map( + (k, v) => MapEntry(k, Interface.fromJson(v as Map)), + ); + + @override + Uint8List serialize(Map data) { + final buffer = runningBuffer = JsonBufferBuilder(); + data.forEach((k, v) => buffer.map[k] = (v as Interface).toJson()); + return runningBuffer!.serialize(); } } diff --git a/pkgs/dart_model/benchmark/sdk_maps_buffer_wire_benchmark.dart b/pkgs/dart_model/benchmark/sdk_maps_buffer_wire_benchmark.dart index 3bf0a492..082114dd 100644 --- a/pkgs/dart_model/benchmark/sdk_maps_buffer_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/sdk_maps_buffer_wire_benchmark.dart @@ -2,6 +2,8 @@ // 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 'dart:typed_data'; + import 'json_buffer.dart'; import 'sdk_maps_json_wire_benchmark.dart'; @@ -9,12 +11,10 @@ import 'sdk_maps_json_wire_benchmark.dart'; /// [JsonBuffer]. class SdkMapsBufferWireBenchmark extends SdkMapsJsonWireBenchmark { @override - void run() { - serialized = JsonBuffer(createData()).serialize(); - } + Uint8List serialize(Map data) => + JsonBuffer(data).serialize(); @override - void deserialize() { - deserialized = JsonBuffer.deserialize(serialized!).asMap; - } + Map deserialize(Uint8List serialized) => + JsonBuffer.deserialize(serialized).asMap; } diff --git a/pkgs/dart_model/benchmark/sdk_maps_builder_wire_benchmark.dart b/pkgs/dart_model/benchmark/sdk_maps_builder_wire_benchmark.dart index 289bddbe..ed22e065 100644 --- a/pkgs/dart_model/benchmark/sdk_maps_builder_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/sdk_maps_builder_wire_benchmark.dart @@ -2,6 +2,8 @@ // 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 'dart:typed_data'; + import 'package:dart_model/src/json_buffer/json_buffer_builder.dart'; import 'sdk_maps_json_wire_benchmark.dart'; @@ -10,14 +12,13 @@ import 'sdk_maps_json_wire_benchmark.dart'; /// [JsonBufferBuilder]. class SdkMapsBuilderWireBenchmark extends SdkMapsJsonWireBenchmark { @override - void run() { + Uint8List serialize(Map data) { final builder = JsonBufferBuilder(); - builder.map.addAll(createData()); - serialized = builder.serialize(); + builder.map.addAll(data); + return builder.serialize(); } @override - void deserialize() { - deserialized = JsonBufferBuilder.deserialize(serialized!).map; - } + Map deserialize(Uint8List serialized) => + JsonBufferBuilder.deserialize(serialized).map; } diff --git a/pkgs/dart_model/benchmark/sdk_maps_json_wire_benchmark.dart b/pkgs/dart_model/benchmark/sdk_maps_json_wire_benchmark.dart index 81c8e384..5e8a35d7 100644 --- a/pkgs/dart_model/benchmark/sdk_maps_json_wire_benchmark.dart +++ b/pkgs/dart_model/benchmark/sdk_maps_json_wire_benchmark.dart @@ -10,10 +10,10 @@ import 'serialization_benchmark.dart'; /// Benchmark accumulating data into SDK maps then serializing it to JSON. class SdkMapsJsonWireBenchmark extends SerializationBenchmark { @override - void run() { - serialized = json.fuse(utf8).encode(createData()) as Uint8List; - } + Uint8List serialize(Map data) => + json.fuse(utf8).encode(data) as Uint8List; + @override Map createData() { return Map.fromIterable( mapKeys, @@ -53,8 +53,8 @@ class SdkMapsJsonWireBenchmark extends SerializationBenchmark { } @override - void deserialize() { - deserialized = json.fuse(utf8).decode(serialized!) as Map; + Map deserialize(Uint8List serialized) { + return json.fuse(utf8).decode(serialized) as Map; } } diff --git a/pkgs/dart_model/benchmark/serialization_benchmark.dart b/pkgs/dart_model/benchmark/serialization_benchmark.dart index 8f35dc60..dcabc1ba 100644 --- a/pkgs/dart_model/benchmark/serialization_benchmark.dart +++ b/pkgs/dart_model/benchmark/serialization_benchmark.dart @@ -13,69 +13,93 @@ final mapKeys = List.generate(mapSize, (i) => i.toString()); /// Benchmark serializing `dart_model` data. abstract class SerializationBenchmark extends BenchmarkBase { - /// The serialized result; used to report the size. - Uint8List? serialized; + late BenchmarkStage stage; - /// The deserialized result; used to check correctness. - Map? deserialized; + /// The result of [createData]; passed to [serialize] later. + late Map _data; - SerializationBenchmark() : super(''); - - @override - String get name => runtimeType.toString(); + /// The result of [serialize]; used to report the size and passed to + /// [deserialize] later. + late Uint8List _serialized; - /// For [ProcessBenchmark]. - void deserialize(); + /// The result of [deserialize]; used to check correctness and passed to + /// [deepHash] later. + late Map deserialized; - /// Creates a [ProcessBenchmark] based on the deserialized data. - ProcessBenchmark processBenchmark() => ProcessBenchmark(this); + /// The result of [deepHash]; result should be identical from all + /// implementations. + late int hashResult; - List makeMemberNames(int key) { - final length = key % 10; - return List.generate( - // "key % 2999" so some member names are reused. - length, - (i) => 'interface${key % 2999}member$i', - ); - } -} - -/// Benchmark that walks the full deserialized data. -class ProcessBenchmark extends BenchmarkBase { - final SerializationBenchmark serializationBenchmark; - - /// The hash of all the data; used to check correctness. - int? computedResult; + SerializationBenchmark() : super(''); - ProcessBenchmark(this.serializationBenchmark) : super(''); + /// The length of the serialized bytes, only valid to call after running + /// [BenchmarkStage.serialize]. + int get bytes => _serialized.lengthInBytes; @override - String get name => 'Process${serializationBenchmark.name}'; + String get name => '$runtimeType-${stage.name}'; @override void run() { - computedResult = deepHash(serializationBenchmark.deserialized!); + switch (stage) { + case BenchmarkStage.create: + _data = createData(); + case BenchmarkStage.serialize: + _serialized = serialize(_data); + case BenchmarkStage.deserialize: + deserialized = deserialize(_serialized); + case BenchmarkStage.process: + hashResult = deepHash(deserialized); + } } - int deepHash(Map map) { + /// Used to measure [BenchmarkStage.create], sets [_data] to the result. + Map createData(); + + /// Used to measure [BenchmarkStage.serialize], called with [data], sets + /// [_serialized] to the result. + Uint8List serialize(Map data); + + /// Used to measure [BenchmarkStage.deserialize], called with + /// [_serialized], sets [deserialized] to the result. + Map deserialize(Uint8List data); + + /// Used to measure [BenchmarkStage.process], called with + /// [_serialized], sets [deserialized] to the result. + /// + /// Default implementation works only for JSON style maps. + int deepHash(Map deserialized) { var result = 0; - for (final entry in map.entries) { + for (final entry in deserialized.entries) { result ^= entry.key.hashCode; final value = entry.value; - if (value is Map) { - result ^= deepHash(value); - } else if (value is wrapped.Serializable) { - result ^= deepHash(value.toJson()); - } else if (value is Hashable) { - result ^= value.deepHash; - } else { - result ^= value.hashCode; - } + result ^= switch (value) { + Map() => deepHash(value), + Hashable() => value.deepHash, + wrapped.Serializable() => deepHash(value.toJson()), + String() || int() || bool() => value.hashCode, + _ => + throw StateError( + 'Unrecognized JSON value $value, ' + 'custom hash function needed?', + ), + }; } return result; } + + List makeMemberNames(int key) { + final length = key % 10; + return List.generate( + // "key % 2999" so some member names are reused. + length, + (i) => 'interface${key % 2999}member$i', + ); + } } +enum BenchmarkStage { create, serialize, deserialize, process } + /// Interface for computing a hash, when the underlying object isn't a Map. abstract interface class Hashable { int get deepHash;