Comparison performance testing between Pbandk and Kotlinx Protobuf
Introduction
This article is a comparison of the performance of the serialization and deserialization of the same data using the Pbandk library and the kotlinx-protobuf library.
Test data
@file:OptIn(PublicForGeneratedCode::class)
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumber
import pbandk.*
import kotlin.time.measureTime
@Serializable
data class KotlinxTest(
@ProtoNumber(1) val i: UInt = 0u,
@ProtoNumber(2) val l: ULong = 0u,
@ProtoNumber(3) val bytes: ByteArray,
)
@pbandk.Export
data class PbandkTest(
val i: Int = 0,
val l: Long = 0,
val bytes: ByteArr? = null,
) : Message {
override val unknownFields: Map<Int, pbandk.UnknownField> = emptyMap()
override val descriptor: MessageDescriptor<PbandkTest> get() = Companion.descriptor
override val protoSize: Int by lazy { super.protoSize }
override fun plus(other: Message?): Message = this
companion object : Message.Companion<PbandkTest> {
override fun decodeWith(u: pbandk.MessageDecoder): PbandkTest = PbandkTest.decodeWithImpl(u)
override val descriptor: MessageDescriptor<PbandkTest> by lazy {
val fieldsList = ArrayList<FieldDescriptor<PbandkTest, *>>(3).apply {
add(
FieldDescriptor(
messageDescriptor = this@Companion::descriptor,
name = "i",
number = 1,
type = FieldDescriptor.Type.Primitive.UInt32(),
jsonName = "i",
value = PbandkTest::i
)
)
add(
FieldDescriptor(
messageDescriptor = this@Companion::descriptor,
name = "l",
number = 2,
type = FieldDescriptor.Type.Primitive.UInt64(),
jsonName = "l",
value = PbandkTest::l
)
)
add(
FieldDescriptor(
messageDescriptor = this@Companion::descriptor,
name = "bytes",
number = 3,
type = FieldDescriptor.Type.Primitive.Bytes(),
jsonName = "bytes",
value = PbandkTest::bytes
)
)
}
MessageDescriptor(
fullName = "PbandkTest",
messageClass = PbandkTest::class,
messageCompanion = this,
fields = fieldsList
)
}
}
}
@Suppress("UNCHECKED_CAST")
private fun PbandkTest.Companion.decodeWithImpl(u: pbandk.MessageDecoder): PbandkTest {
var i = 0
var j = 0L
var bytes: ByteArr? = null
val unknownFields = u.readMessage(this) { fieldNumber, fieldValue ->
when (_fieldNumber) {
1 -> i = _fieldValue as Int
2 -> j = _fieldValue as Long
3 -> bytes = _fieldValue as ByteArr
}
}
return PbandkTest(i, j, bytes)
}
suspend fun main(args : Array<String>){
println("KTX_PB: " + measureTime {
var i = 1u
repeat(10000000) {
val bytes = ProtoBuf.encodeToByteArray(KotlinxTest(i, 2u, byteArrayOf(1, 2, 3)))
val obj = ProtoBuf.decodeFromByteArray<KotlinxTest>(bytes)
i += obj.i
}
})
println("PBDANK_PB: " + measureTime {
var i = 1
repeat(10000000) {
val bytes = PbandkTest(i, 2, ByteArr(byteArrayOf(1, 2, 3))).encodeToByteArray()
val obj = PbandkTest.decodeFromByteArray(bytes)
i += obj.i
}
})
}
Test result
# First
13:29:11: 正在执行 'jvmRun -DmainClass=MainJvmKt --quiet'…
KTX_PB: 697.140600ms
PBDANK_PB: 1.191587100s
13:29:14: 执行完成 'jvmRun -DmainClass=MainJvmKt --quiet'。
# Second
13:33:15: 正在执行 'jvmRun -DmainClass=MainJvmKt --quiet'…
KTX_PB: 687.145800ms
PBDANK_PB: 1.180827900s
13:33:18: 执行完成 'jvmRun -DmainClass=MainJvmKt --quiet'。
Conclusion
The kotlinx-protobuf library is faster than the Pbandk library in serialization and deserialization.
Moreover, pbandk's support for various types of kotlin is not friendly, and it belongs to a garbage project written by kotlin.
I found a lot of use of this garbage serialization library in Tencent's kuikly
project. kotlinx protobuf does not provide the function of one click conversion from proto to kotlin data class
.
I guess Tencent's so-called programmers used this ugly, high-performance, and extremely high garbage proto serialization framework to save effort.