From d62f3b8a4b5292dac2575b846b5fd4cfe679ce65 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 13 Dec 2024 08:49:13 -0500 Subject: [PATCH] Speedup ProtoWriteType.from (#2879) * Added map-related protobuf benchmark * Reimplemented ProtoWireType lookup --- .../benchmarks/protobuf/ProtoMapBenchmark.kt | 32 +++++++++++++++++++ .../protobuf/internal/Helpers.kt | 13 ++++++-- .../protobuf/internal/ProtobufReader.kt | 2 +- 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoMapBenchmark.kt diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoMapBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoMapBenchmark.kt new file mode 100644 index 0000000000..f3de66d4eb --- /dev/null +++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoMapBenchmark.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.benchmarks.protobuf + +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf +import org.openjdk.jmh.annotations.* +import java.util.concurrent.TimeUnit + +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(1) +open class ProtoMapBenchmark { + + @Serializable + class Holder(val map: Map) + + private val value = Holder((0..128).associateBy { it.toString() }) + private val bytes = ProtoBuf.encodeToByteArray(value) + + @Benchmark + fun toBytes() = ProtoBuf.encodeToByteArray(Holder.serializer(), value) + + @Benchmark + fun fromBytes() = ProtoBuf.decodeFromByteArray(Holder.serializer(), bytes) +} diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt index ea6d4b6823..33e1c78ccd 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt @@ -22,8 +22,17 @@ internal enum class ProtoWireType(val typeId: Int) { ; companion object { - fun from(typeId: Int): ProtoWireType { - return ProtoWireType.entries.find { it.typeId == typeId } ?: INVALID + private val entryArray = Array(8) { typeId -> + ProtoWireType.entries.find { it.typeId == typeId } ?: INVALID + } + + /** + * Extracts three least significant bits from the [value] and + * returns [ProtoWireType] with corresponding type id, or [ProtoWireType.INVALID] + * if there are no such a type. + */ + fun fromLeastSignificantBits(value: Int): ProtoWireType { + return entryArray[value and 0b111] } } diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt index 5b8ce1c292..674224a718 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt @@ -42,7 +42,7 @@ internal class ProtobufReader(private val input: ByteArrayInput) { -1 } else { currentId = header ushr 3 - currentType = ProtoWireType.from(header and 0b111) + currentType = ProtoWireType.fromLeastSignificantBits(header) currentId } }