Skip to content

Commit daa95c7

Browse files
authored
Added benchmarks on cacheable child serializers
Relates #1918
1 parent 24ac6b8 commit daa95c7

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package kotlinx.benchmarks.json
2+
3+
import kotlinx.serialization.*
4+
import kotlinx.serialization.descriptors.SerialDescriptor
5+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
6+
import kotlinx.serialization.descriptors.element
7+
import kotlinx.serialization.encoding.*
8+
import kotlinx.serialization.json.*
9+
import kotlinx.serialization.modules.*
10+
import org.openjdk.jmh.annotations.*
11+
import java.util.concurrent.*
12+
13+
@Warmup(iterations = 7, time = 1)
14+
@Measurement(iterations = 5, time = 1)
15+
@BenchmarkMode(Mode.Throughput)
16+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
17+
@State(Scope.Benchmark)
18+
@Fork(1)
19+
open class ContextualOverheadBenchmark {
20+
@Serializable
21+
data class Holder(val data: @Contextual Data)
22+
23+
class Data(val a: Int, val b: String)
24+
25+
object DataSerializer: KSerializer<Data> {
26+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Serializer") {
27+
element<Int>("a")
28+
element<String>("b")
29+
}
30+
31+
override fun deserialize(decoder: Decoder): Data {
32+
return decoder.decodeStructure(descriptor) {
33+
var a = 0
34+
var b = ""
35+
while (true) {
36+
when (val index = decodeElementIndex(descriptor)) {
37+
0 -> a = decodeIntElement(descriptor, 0)
38+
1 -> b = decodeStringElement(descriptor, 1)
39+
CompositeDecoder.DECODE_DONE -> break
40+
else -> error("Unexpected index: $index")
41+
}
42+
}
43+
Data(a, b)
44+
}
45+
}
46+
47+
override fun serialize(encoder: Encoder, value: Data) {
48+
encoder.encodeStructure(descriptor) {
49+
encodeIntElement(descriptor, 0, value.a)
50+
encodeStringElement(descriptor, 1, value.b)
51+
}
52+
}
53+
54+
}
55+
56+
private val module = SerializersModule {
57+
contextual(DataSerializer)
58+
}
59+
60+
private val json = Json { serializersModule = module }
61+
62+
private val holder = Holder(Data(1, "abc"))
63+
private val holderString = json.encodeToString(holder)
64+
private val holderSerializer = serializer<Holder>()
65+
66+
@Benchmark
67+
fun decode() = json.decodeFromString(holderSerializer, holderString)
68+
69+
@Benchmark
70+
fun encode() = json.encodeToString(holderSerializer, holder)
71+
72+
}

benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PolymorphismOverheadBenchmark.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ open class PolymorphismOverheadBenchmark {
1818
@JsonClassDiscriminator("poly")
1919
data class PolymorphicWrapper(val i: @Polymorphic Poly, val i2: Impl) // amortize the cost a bit
2020

21+
@Serializable
22+
data class SimpleWrapper(val poly: @Polymorphic Poly)
23+
2124
@Serializable
2225
data class BaseWrapper(val i: Impl, val i2: Impl)
2326

@@ -40,6 +43,11 @@ open class PolymorphismOverheadBenchmark {
4043
private val polyString = json.encodeToString<Poly>(impl)
4144
private val serializer = serializer<Poly>()
4245

46+
private val wrapper = SimpleWrapper(Impl(1, "abc"))
47+
private val wrapperString = json.encodeToString(wrapper)
48+
private val wrapperSerializer = serializer<SimpleWrapper>()
49+
50+
4351
// 5000
4452
@Benchmark
4553
fun base() = json.decodeFromString(Impl.serializer(), implString)
@@ -51,4 +59,12 @@ open class PolymorphismOverheadBenchmark {
5159
@Benchmark
5260
fun poly() = json.decodeFromString(serializer, polyString)
5361

62+
// test for child polymorphic serializer in decoding
63+
@Benchmark
64+
fun polyChildDecode() = json.decodeFromString(wrapperSerializer, wrapperString)
65+
66+
// test for child polymorphic serializer in encoding
67+
@Benchmark
68+
fun polyChildEncode() = json.encodeToString(wrapperSerializer, wrapper)
69+
5470
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
@file:UseSerializers(UseSerializerOverheadBenchmark.DataClassSerializer::class, UseSerializerOverheadBenchmark.DataObjectSerializer::class)
2+
package kotlinx.benchmarks.json
3+
4+
5+
import kotlinx.serialization.*
6+
import kotlinx.serialization.descriptors.SerialDescriptor
7+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
8+
import kotlinx.serialization.descriptors.element
9+
import kotlinx.serialization.encoding.*
10+
import kotlinx.serialization.json.*
11+
import kotlinx.serialization.modules.*
12+
import org.openjdk.jmh.annotations.*
13+
import java.util.concurrent.*
14+
15+
@Warmup(iterations = 7, time = 1)
16+
@Measurement(iterations = 5, time = 1)
17+
@BenchmarkMode(Mode.Throughput)
18+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
19+
@State(Scope.Benchmark)
20+
@Fork(1)
21+
open class UseSerializerOverheadBenchmark {
22+
@Serializable
23+
data class HolderForClass(val data: DataForClass)
24+
25+
@Serializable
26+
data class HolderForObject(val data: DataForObject)
27+
28+
class DataForClass(val a: Int, val b: String)
29+
30+
class DataForObject(val a: Int, val b: String)
31+
32+
object DataClassSerializer: KSerializer<DataForClass> {
33+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ClassSerializer") {
34+
element<Int>("a")
35+
element<String>("b")
36+
}
37+
38+
override fun deserialize(decoder: Decoder): DataForClass {
39+
return decoder.decodeStructure(descriptor) {
40+
var a = 0
41+
var b = ""
42+
while (true) {
43+
when (val index = decodeElementIndex(ContextualOverheadBenchmark.DataSerializer.descriptor)) {
44+
0 -> a = decodeIntElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 0)
45+
1 -> b = decodeStringElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 1)
46+
CompositeDecoder.DECODE_DONE -> break
47+
else -> error("Unexpected index: $index")
48+
}
49+
}
50+
DataForClass(a, b)
51+
}
52+
}
53+
54+
override fun serialize(encoder: Encoder, value: DataForClass) {
55+
encoder.encodeStructure(descriptor) {
56+
encodeIntElement(descriptor, 0, value.a)
57+
encodeStringElement(descriptor, 1, value.b)
58+
}
59+
}
60+
}
61+
62+
object DataObjectSerializer: KSerializer<DataForObject> {
63+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ObjectSerializer") {
64+
element<Int>("a")
65+
element<String>("b")
66+
}
67+
68+
override fun deserialize(decoder: Decoder): DataForObject {
69+
return decoder.decodeStructure(descriptor) {
70+
var a = 0
71+
var b = ""
72+
while (true) {
73+
when (val index = decodeElementIndex(ContextualOverheadBenchmark.DataSerializer.descriptor)) {
74+
0 -> a = decodeIntElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 0)
75+
1 -> b = decodeStringElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 1)
76+
CompositeDecoder.DECODE_DONE -> break
77+
else -> error("Unexpected index: $index")
78+
}
79+
}
80+
DataForObject(a, b)
81+
}
82+
}
83+
84+
override fun serialize(encoder: Encoder, value: DataForObject) {
85+
encoder.encodeStructure(descriptor) {
86+
encodeIntElement(descriptor, 0, value.a)
87+
encodeStringElement(descriptor, 1, value.b)
88+
}
89+
}
90+
}
91+
92+
private val module = SerializersModule {
93+
contextual(DataClassSerializer)
94+
}
95+
96+
private val json = Json { serializersModule = module }
97+
98+
private val classHolder = HolderForClass(DataForClass(1, "abc"))
99+
private val classHolderString = json.encodeToString(classHolder)
100+
private val classHolderSerializer = serializer<HolderForClass>()
101+
102+
private val objectHolder = HolderForObject(DataForObject(1, "abc"))
103+
private val objectHolderString = json.encodeToString(objectHolder)
104+
private val objectHolderSerializer = serializer<HolderForObject>()
105+
106+
@Benchmark
107+
fun decodeForClass() = json.decodeFromString(classHolderSerializer, classHolderString)
108+
109+
@Benchmark
110+
fun encodeForClass() = json.encodeToString(classHolderSerializer, classHolder)
111+
112+
/*
113+
Any optimizations should not affect the speed of these tests.
114+
It doesn't make sense to cache singleton (`object`) serializer, because the object is accessed instantly
115+
*/
116+
117+
@Benchmark
118+
fun decodeForObject() = json.decodeFromString(objectHolderSerializer, objectHolderString)
119+
120+
@Benchmark
121+
fun encodeForObject() = json.encodeToString(objectHolderSerializer, objectHolder)
122+
123+
}

0 commit comments

Comments
 (0)