summaryrefslogtreecommitdiff
path: root/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/UseSerializerOverheadBenchmark.kt
blob: bc3c89d709749111248bbf215e4405d1dd3afc2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@file:UseSerializers(UseSerializerOverheadBenchmark.DataClassSerializer::class, UseSerializerOverheadBenchmark.DataObjectSerializer::class)
package kotlinx.benchmarks.json


import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
import org.openjdk.jmh.annotations.*
import java.util.concurrent.*

@Warmup(iterations = 7, time = 1)
@Measurement(iterations = 5, time = 1)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(1)
open class UseSerializerOverheadBenchmark {
    @Serializable
    data class HolderForClass(val data: DataForClass)

    @Serializable
    data class HolderForObject(val data: DataForObject)

    class DataForClass(val a: Int, val b: String)

    class DataForObject(val a: Int, val b: String)

    object DataClassSerializer: KSerializer<DataForClass> {
        override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ClassSerializer") {
            element<Int>("a")
            element<String>("b")
        }

        override fun deserialize(decoder: Decoder): DataForClass {
            return decoder.decodeStructure(descriptor) {
                var a = 0
                var b = ""
                while (true) {
                    when (val index = decodeElementIndex(ContextualOverheadBenchmark.DataSerializer.descriptor)) {
                        0 -> a = decodeIntElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 0)
                        1 -> b = decodeStringElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 1)
                        CompositeDecoder.DECODE_DONE -> break
                        else -> error("Unexpected index: $index")
                    }
                }
                DataForClass(a, b)
            }
        }

        override fun serialize(encoder: Encoder, value: DataForClass) {
            encoder.encodeStructure(descriptor) {
                encodeIntElement(descriptor, 0, value.a)
                encodeStringElement(descriptor, 1, value.b)
            }
        }
    }

    object DataObjectSerializer: KSerializer<DataForObject> {
        override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ObjectSerializer") {
            element<Int>("a")
            element<String>("b")
        }

        override fun deserialize(decoder: Decoder): DataForObject {
            return decoder.decodeStructure(descriptor) {
                var a = 0
                var b = ""
                while (true) {
                    when (val index = decodeElementIndex(ContextualOverheadBenchmark.DataSerializer.descriptor)) {
                        0 -> a = decodeIntElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 0)
                        1 -> b = decodeStringElement(ContextualOverheadBenchmark.DataSerializer.descriptor, 1)
                        CompositeDecoder.DECODE_DONE -> break
                        else -> error("Unexpected index: $index")
                    }
                }
                DataForObject(a, b)
            }
        }

        override fun serialize(encoder: Encoder, value: DataForObject) {
            encoder.encodeStructure(descriptor) {
                encodeIntElement(descriptor, 0, value.a)
                encodeStringElement(descriptor, 1, value.b)
            }
        }
    }

    private val module = SerializersModule {
        contextual(DataClassSerializer)
    }

    private val json = Json { serializersModule = module }

    private val classHolder = HolderForClass(DataForClass(1, "abc"))
    private val classHolderString = json.encodeToString(classHolder)
    private val classHolderSerializer = serializer<HolderForClass>()

    private val objectHolder = HolderForObject(DataForObject(1, "abc"))
    private val objectHolderString = json.encodeToString(objectHolder)
    private val objectHolderSerializer = serializer<HolderForObject>()

    @Benchmark
    fun decodeForClass() = json.decodeFromString(classHolderSerializer, classHolderString)

    @Benchmark
    fun encodeForClass() = json.encodeToString(classHolderSerializer, classHolder)

    /*
    Any optimizations should not affect the speed of these tests.
    It doesn't make sense to cache singleton (`object`) serializer, because the object is accessed instantly
     */

    @Benchmark
    fun decodeForObject() = json.decodeFromString(objectHolderSerializer, objectHolderString)

    @Benchmark
    fun encodeForObject() = json.encodeToString(objectHolderSerializer, objectHolder)

}