From 11cd8fd41923ae32bcffd7c01dfbed9fcb2b6583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Fri, 9 May 2025 15:42:50 +0100 Subject: [PATCH] Add sparse enum decoding benchmarks With upcoming change we'll improve decoding performance of enums, but there will be a difference between "sparse" and "dense" enum decoding performance even though they'll both be faster. To be able to measure the difference add a "sparse" enum type and a benchmark for decoding it. "Sparse" means the enum has large gaps between known enum values, or negative enum values. When decoding this kind of enums, the mapping from the wire `varint` to the Dart value for the enum needs to be done by binary search, map lookup, or similar. For "dense" enums, we can have a list of enum values and index the list directly with the `varint` value, after a range check. These changes will be done in the follow-up PR(s). --- benchmarks/bin/binary_decode_packed.dart | 20 ++++++++++++++++++++ benchmarks/protos/packed_fields.proto | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/benchmarks/bin/binary_decode_packed.dart b/benchmarks/bin/binary_decode_packed.dart index f640cca4..a09c8bc1 100644 --- a/benchmarks/bin/binary_decode_packed.dart +++ b/benchmarks/bin/binary_decode_packed.dart @@ -181,6 +181,25 @@ class PackedEnumDecodingBenchmark extends BenchmarkBase { } } +class PackedSparseEnumDecodingBenchmark extends BenchmarkBase { + late final Uint8List encoded; + + PackedSparseEnumDecodingBenchmark() : super('PackedSparseEnumDecoding') { + final rand = Random(123); + final message = PackedFields(); + final numEnums = SparseEnum.values.length; + for (var i = 0; i < 1000000; i += 1) { + message.sparseEnum.add(SparseEnum.values[rand.nextInt(numEnums)]); + } + encoded = message.writeToBuffer(); + } + + @override + void run() { + sink = PackedFields()..mergeFromBuffer(encoded); + } +} + void main() { PackedInt32DecodingBenchmark().report(); PackedInt64DecodingBenchmark().report(); @@ -190,6 +209,7 @@ void main() { PackedSint64DecodingBenchmark().report(); PackedBoolDecodingBenchmark().report(); PackedEnumDecodingBenchmark().report(); + PackedSparseEnumDecodingBenchmark().report(); if (int.parse('1') == 0) print(sink); } diff --git a/benchmarks/protos/packed_fields.proto b/benchmarks/protos/packed_fields.proto index 09d32d3b..18f76394 100644 --- a/benchmarks/protos/packed_fields.proto +++ b/benchmarks/protos/packed_fields.proto @@ -10,6 +10,7 @@ message PackedFields { repeated bool packedBool = 7 [packed = true]; repeated Enum1 packedEnum1 = 8 [packed = true]; repeated Enum2 packedEnum2 = 9 [packed = true]; + repeated SparseEnum sparseEnum = 10 [packed = true]; } enum Enum1 { @@ -27,3 +28,23 @@ enum Enum2 { ENUM_2_4 = 4; ENUM_2_5 = 5; } + +// An enum with large gaps between the known values, and with negative values. +// +// This will be slower to decode as the varint to enum value mapping needs to be +// done with binary search, or map lookup etc. +enum SparseEnum { + ENUM_ZERO = 0; + ENUM_MIN_INT = -2147483648; + ENUM_1 = -1000000000; + ENUM_2 = -100000000; + ENUM_3 = -10000000; + ENUM_4 = -1000000; + ENUM_5 = -100000; + ENUM_6 = 100000; + ENUM_7 = 1000000; + ENUM_8 = 10000000; + ENUM_9 = 100000000; + ENUM_10 = 1000000000; + ENUM_MAX_INT = 2147483647; +}