summaryrefslogtreecommitdiff
path: root/benchmark
diff options
context:
space:
mode:
Diffstat (limited to 'benchmark')
-rw-r--r--benchmark/build.gradle42
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ContextualOverheadBenchmark.kt72
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt28
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/LookupOverheadBenchmark.kt69
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PolymorphismOverheadBenchmark.kt70
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt10
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt25
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/UseSerializerOverheadBenchmark.kt123
-rw-r--r--benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitterUntailored.kt170
9 files changed, 594 insertions, 15 deletions
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
index 8e0e4927..751ad78c 100644
--- a/benchmark/build.gradle
+++ b/benchmark/build.gradle
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
/*
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
@@ -6,27 +8,45 @@ apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlinx-serialization'
apply plugin: 'idea'
-apply plugin: 'net.ltgt.apt'
apply plugin: 'com.github.johnrengelman.shadow'
-apply plugin: 'me.champeau.gradle.jmh'
+apply plugin: 'me.champeau.jmh'
sourceCompatibility = 1.8
targetCompatibility = 1.8
-jmh.jmhVersion = 1.22
+jmh.jmhVersion = "1.35"
+
+processJmhResources {
+ doFirst {
+ duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
+ }
+}
jmhJar {
- baseName 'benchmarks'
- classifier = null
- version = null
- destinationDir = file("$rootDir")
+ archiveBaseName.set('benchmarks')
+ archiveVersion.set('')
+ destinationDirectory = file("$rootDir")
+}
+
+// to include benchmark-module jmh source set compilation during build to verify that it is also compiled succesfully
+assemble.dependsOn jmhClasses
+
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ if (rootProject.ext.kotlin_lv_override != null) {
+ languageVersion = rootProject.ext.kotlin_lv_override
+ freeCompilerArgs += "-Xsuppress-version-warnings"
+ }
+ }
}
dependencies {
- implementation 'org.openjdk.jmh:jmh-core:1.22'
- implementation 'com.google.guava:guava:24.1.1-jre'
- implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1'
- implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1'
+ implementation 'org.openjdk.jmh:jmh-core:1.35'
+ implementation 'com.google.guava:guava:31.1-jre'
+ implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
+ implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3'
+ implementation "com.squareup.okio:okio:$okio_version"
implementation project(':kotlinx-serialization-core')
implementation project(':kotlinx-serialization-json')
+ implementation project(':kotlinx-serialization-json-okio')
implementation project(':kotlinx-serialization-protobuf')
}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ContextualOverheadBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ContextualOverheadBenchmark.kt
new file mode 100644
index 00000000..2773cfc2
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/ContextualOverheadBenchmark.kt
@@ -0,0 +1,72 @@
+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 ContextualOverheadBenchmark {
+ @Serializable
+ data class Holder(val data: @Contextual Data)
+
+ class Data(val a: Int, val b: String)
+
+ object DataSerializer: KSerializer<Data> {
+ override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Serializer") {
+ element<Int>("a")
+ element<String>("b")
+ }
+
+ override fun deserialize(decoder: Decoder): Data {
+ return decoder.decodeStructure(descriptor) {
+ var a = 0
+ var b = ""
+ while (true) {
+ when (val index = decodeElementIndex(descriptor)) {
+ 0 -> a = decodeIntElement(descriptor, 0)
+ 1 -> b = decodeStringElement(descriptor, 1)
+ CompositeDecoder.DECODE_DONE -> break
+ else -> error("Unexpected index: $index")
+ }
+ }
+ Data(a, b)
+ }
+ }
+
+ override fun serialize(encoder: Encoder, value: Data) {
+ encoder.encodeStructure(descriptor) {
+ encodeIntElement(descriptor, 0, value.a)
+ encodeStringElement(descriptor, 1, value.b)
+ }
+ }
+
+ }
+
+ private val module = SerializersModule {
+ contextual(DataSerializer)
+ }
+
+ private val json = Json { serializersModule = module }
+
+ private val holder = Holder(Data(1, "abc"))
+ private val holderString = json.encodeToString(holder)
+ private val holderSerializer = serializer<Holder>()
+
+ @Benchmark
+ fun decode() = json.decodeFromString(holderSerializer, holderString)
+
+ @Benchmark
+ fun encode() = json.encodeToString(holderSerializer, holder)
+
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
index b8125001..d162418c 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
@@ -4,7 +4,12 @@ import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.module.kotlin.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
+import kotlinx.serialization.json.okio.encodeToBufferedSink
+import okio.blackholeSink
+import okio.buffer
import org.openjdk.jmh.annotations.*
+import java.io.ByteArrayInputStream
+import java.io.OutputStream
import java.util.concurrent.*
@Warmup(iterations = 7, time = 1)
@@ -63,7 +68,15 @@ open class JacksonComparisonBenchmark {
cookies = "_ga=GA1.2.971852807.1546968515"
)
+ private val devNullSink = blackholeSink().buffer()
+ private val devNullStream = object : OutputStream() {
+ override fun write(b: Int) {}
+ override fun write(b: ByteArray) {}
+ override fun write(b: ByteArray, off: Int, len: Int) {}
+ }
+
private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+ private val utf8BytesData = stringData.toByteArray()
@Serializable
private class SmallDataClass(val id: Int, val name: String)
@@ -83,12 +96,27 @@ open class JacksonComparisonBenchmark {
fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), data)
@Benchmark
+ fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), data, devNullStream)
+
+ @Benchmark
+ fun kotlinFromStream() = Json.decodeFromStream(DefaultPixelEvent.serializer(), ByteArrayInputStream(utf8BytesData))
+
+ @Benchmark
+ fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), data, devNullSink)
+
+ @Benchmark
fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), dataWithEscapes)
@Benchmark
fun kotlinSmallToString(): String = Json.encodeToString(SmallDataClass.serializer(), smallData)
@Benchmark
+ fun kotlinSmallToStream() = Json.encodeToStream(SmallDataClass.serializer(), smallData, devNullStream)
+
+ @Benchmark
+ fun kotlinSmallToOkio() = Json.encodeToBufferedSink(SmallDataClass.serializer(), smallData, devNullSink)
+
+ @Benchmark
fun jacksonFromString(): DefaultPixelEvent = objectMapper.readValue(stringData, DefaultPixelEvent::class.java)
@Benchmark
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/LookupOverheadBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/LookupOverheadBenchmark.kt
new file mode 100644
index 00000000..ddfc5792
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/LookupOverheadBenchmark.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.benchmarks.json
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 7, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class LookupOverheadBenchmark {
+
+ @Serializable
+ class Holder(val a: String)
+
+ @Serializable
+ class Generic<T>(val a: T)
+
+ @Serializable
+ class DoubleGeneric<T1, T2>(val a: T1, val b: T2)
+
+ @Serializable
+ class PentaGeneric<T1, T2, T3, T4, T5>(val a: T1, val b: T2, val c: T3, val d: T4, val e: T5)
+
+ private val data = """{"a":""}"""
+ private val doubleData = """{"a":"","b":0}"""
+ private val pentaData = """{"a":"","b":0,"c":1,"d":true,"e":" "}"""
+
+ @Serializable
+ object Object
+
+ @Benchmark
+ fun dataReified() = Json.decodeFromString<Holder>(data)
+
+ @Benchmark
+ fun dataPlain() = Json.decodeFromString(Holder.serializer(), data)
+
+ @Benchmark
+ fun genericReified() = Json.decodeFromString<Generic<String>>(data)
+
+ @Benchmark
+ fun genericPlain() = Json.decodeFromString(Generic.serializer(String.serializer()), data)
+
+ @Benchmark
+ fun doubleGenericReified() = Json.decodeFromString<DoubleGeneric<String, Int>>(doubleData)
+
+ @Benchmark
+ fun doubleGenericPlain() = Json.decodeFromString(DoubleGeneric.serializer(String.serializer(), Int.serializer()), doubleData)
+
+ @Benchmark
+ fun pentaGenericReified() = Json.decodeFromString<PentaGeneric<String, Int, Long, Boolean, Char>>(pentaData)
+
+ @Benchmark
+ fun pentaGenericPlain() = Json.decodeFromString(PentaGeneric.serializer(String.serializer(), Int.serializer(), Long.serializer(), Boolean.serializer(), Char.serializer()), pentaData)
+
+ @Benchmark
+ fun objectReified() = Json.decodeFromString<Object>("{}")
+
+ @Benchmark
+ fun objectPlain() = Json.decodeFromString(Object.serializer(), "{}")
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PolymorphismOverheadBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PolymorphismOverheadBenchmark.kt
new file mode 100644
index 00000000..9af856d3
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/PolymorphismOverheadBenchmark.kt
@@ -0,0 +1,70 @@
+package kotlinx.benchmarks.json
+
+import kotlinx.serialization.*
+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 PolymorphismOverheadBenchmark {
+
+ @Serializable
+ @JsonClassDiscriminator("poly")
+ data class PolymorphicWrapper(val i: @Polymorphic Poly, val i2: Impl) // amortize the cost a bit
+
+ @Serializable
+ data class SimpleWrapper(val poly: @Polymorphic Poly)
+
+ @Serializable
+ data class BaseWrapper(val i: Impl, val i2: Impl)
+
+ @JsonClassDiscriminator("poly")
+ interface Poly
+
+ @Serializable
+ @JsonClassDiscriminator("poly")
+ class Impl(val a: Int, val b: String) : Poly
+
+ private val impl = Impl(239, "average_size_string")
+ private val module = SerializersModule {
+ polymorphic(Poly::class) {
+ subclass(Impl.serializer())
+ }
+ }
+
+ private val json = Json { serializersModule = module }
+ private val implString = json.encodeToString(impl)
+ private val polyString = json.encodeToString<Poly>(impl)
+ private val serializer = serializer<Poly>()
+
+ private val wrapper = SimpleWrapper(Impl(1, "abc"))
+ private val wrapperString = json.encodeToString(wrapper)
+ private val wrapperSerializer = serializer<SimpleWrapper>()
+
+
+ // 5000
+ @Benchmark
+ fun base() = json.decodeFromString(Impl.serializer(), implString)
+
+ // As of 1.3.x
+ // Baseline -- 1500
+ // v1, no skip -- 2000
+ // v2, with skip -- 3000 [withdrawn]
+ @Benchmark
+ fun poly() = json.decodeFromString(serializer, polyString)
+
+ // test for child polymorphic serializer in decoding
+ @Benchmark
+ fun polyChildDecode() = json.decodeFromString(wrapperSerializer, wrapperString)
+
+ // test for child polymorphic serializer in encoding
+ @Benchmark
+ fun polyChildEncode() = json.encodeToString(wrapperSerializer, wrapper)
+
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt
index 5c930ec6..90889fe8 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterBenchmark.kt
@@ -3,6 +3,7 @@ package kotlinx.benchmarks.json
import kotlinx.benchmarks.model.*
import kotlinx.serialization.json.*
import org.openjdk.jmh.annotations.*
+import java.io.OutputStream
import java.util.concurrent.*
@Warmup(iterations = 7, time = 1)
@@ -24,6 +25,12 @@ open class TwitterBenchmark {
private val jsonImplicitNulls = Json { explicitNulls = false }
+ private val devNullStream = object : OutputStream() {
+ override fun write(b: Int) {}
+ override fun write(b: ByteArray) {}
+ override fun write(b: ByteArray, off: Int, len: Int) {}
+ }
+
@Setup
fun init() {
require(twitter == Json.decodeFromString(Twitter.serializer(), Json.encodeToString(Twitter.serializer(), twitter)))
@@ -38,4 +45,7 @@ open class TwitterBenchmark {
@Benchmark
fun encodeTwitter() = Json.encodeToString(Twitter.serializer(), twitter)
+
+ @Benchmark
+ fun encodeTwitterStream() = Json.encodeToStream(Twitter.serializer(), twitter, devNullStream)
}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt
index e015ad96..837a8ba3 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedBenchmark.kt
@@ -3,8 +3,6 @@ package kotlinx.benchmarks.json
import kotlinx.benchmarks.model.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
-import kotlinx.serialization.json.Json.Default.decodeFromString
-import kotlinx.serialization.json.Json.Default.encodeToString
import org.openjdk.jmh.annotations.*
import java.util.concurrent.*
@@ -24,19 +22,25 @@ open class TwitterFeedBenchmark {
*/
private val input = TwitterFeedBenchmark::class.java.getResource("/twitter_macro.json").readBytes().decodeToString()
private val twitter = Json.decodeFromString(MacroTwitterFeed.serializer(), input)
+
private val jsonNoAltNames = Json { useAlternativeNames = false }
private val jsonIgnoreUnknwn = Json { ignoreUnknownKeys = true }
- private val jsonIgnoreUnknwnNoAltNames = Json { ignoreUnknownKeys = true; useAlternativeNames = false}
+ private val jsonIgnoreUnknwnNoAltNames = Json { ignoreUnknownKeys = true; useAlternativeNames = false }
+ private val jsonNamingStrategy = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
+ private val jsonNamingStrategyIgnoreUnknwn = Json(jsonNamingStrategy) { ignoreUnknownKeys = true }
+
+ private val twitterKt = jsonNamingStrategy.decodeFromString(MacroTwitterFeedKt.serializer(), input)
@Setup
fun init() {
require(twitter == Json.decodeFromString(MacroTwitterFeed.serializer(), Json.encodeToString(MacroTwitterFeed.serializer(), twitter)))
}
- // Order of magnitude: ~400 op/s
+ // Order of magnitude: ~500 op/s
@Benchmark
fun decodeTwitter() = Json.decodeFromString(MacroTwitterFeed.serializer(), input)
+ // Should be the same as decodeTwitter, since decodeTwitter never hit unknown name and therefore should never build deserializationNamesMap anyway
@Benchmark
fun decodeTwitterNoAltNames() = jsonNoAltNames.decodeFromString(MacroTwitterFeed.serializer(), input)
@@ -46,7 +50,20 @@ open class TwitterFeedBenchmark {
@Benchmark
fun decodeMicroTwitter() = jsonIgnoreUnknwn.decodeFromString(MicroTwitterFeed.serializer(), input)
+ // Should be faster than decodeMicroTwitter, as we explicitly opt-out from deserializationNamesMap on unknown name
@Benchmark
fun decodeMicroTwitterNoAltNames() = jsonIgnoreUnknwnNoAltNames.decodeFromString(MicroTwitterFeed.serializer(), input)
+ // Should be just a bit slower than decodeMicroTwitter, because alternative names map is created in both cases
+ @Benchmark
+ fun decodeMicroTwitterWithNamingStrategy(): MicroTwitterFeedKt = jsonNamingStrategyIgnoreUnknwn.decodeFromString(MicroTwitterFeedKt.serializer(), input)
+
+ // Can be slower than decodeTwitter, as we always build deserializationNamesMap when naming strategy is used
+ @Benchmark
+ fun decodeTwitterWithNamingStrategy(): MacroTwitterFeedKt = jsonNamingStrategy.decodeFromString(MacroTwitterFeedKt.serializer(), input)
+
+ // 15-20% slower than without the strategy. Without serializationNamesMap (invoking strategy on every write), up to 50% slower
+ @Benchmark
+ fun encodeTwitterWithNamingStrategy(): String = jsonNamingStrategy.encodeToString(MacroTwitterFeedKt.serializer(), twitterKt)
+
}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/UseSerializerOverheadBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/UseSerializerOverheadBenchmark.kt
new file mode 100644
index 00000000..bc3c89d7
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/UseSerializerOverheadBenchmark.kt
@@ -0,0 +1,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)
+
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitterUntailored.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitterUntailored.kt
new file mode 100644
index 00000000..fca69cc0
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/MacroTwitterUntailored.kt
@@ -0,0 +1,170 @@
+package kotlinx.benchmarks.model
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+/**
+ * All model classes are the same as in MacroTwitter.kt but named accordingly to Kotlin naming policies to test JsonNamingStrategy performance.
+ * Only Size, SizeType and Urls are not copied
+ */
+
+@Serializable
+data class MacroTwitterFeedKt(
+ val statuses: List<TwitterStatusKt>,
+ val searchMetadata: SearchMetadata
+)
+
+@Serializable
+data class MicroTwitterFeedKt(
+ val statuses: List<TwitterTrimmedStatusKt>
+)
+
+@Serializable
+data class TwitterTrimmedStatusKt(
+ val metadata: MetadataKt,
+ val createdAt: String,
+ val id: Long,
+ val idStr: String,
+ val text: String,
+ val source: String,
+ val truncated: Boolean,
+ val user: TwitterTrimmedUserKt,
+ val retweetedStatus: TwitterTrimmedStatusKt? = null,
+)
+
+@Serializable
+data class TwitterStatusKt(
+ val metadata: MetadataKt,
+ val createdAt: String,
+ val id: Long,
+ val idStr: String,
+ val text: String,
+ val source: String,
+ val truncated: Boolean,
+ val inReplyToStatusId: Long?,
+ val inReplyToStatusIdStr: String?,
+ val inReplyToUserId: Long?,
+ val inReplyToUserIdStr: String?,
+ val inReplyToScreenName: String?,
+ val user: TwitterUserKt,
+ val geo: String?,
+ val coordinates: String?,
+ val place: String?,
+ val contributors: List<String>?,
+ val retweetedStatus: TwitterStatusKt? = null,
+ val retweetCount: Int,
+ val favoriteCount: Int,
+ val entities: StatusEntitiesKt,
+ val favorited: Boolean,
+ val retweeted: Boolean,
+ val lang: String,
+ val possiblySensitive: Boolean? = null
+)
+
+@Serializable
+data class StatusEntitiesKt(
+ val hashtags: List<Hashtag>,
+ val symbols: List<String>,
+ val urls: List<Url>,
+ val userMentions: List<TwitterUserMentionKt>,
+ val media: List<TwitterMediaKt>? = null
+)
+
+@Serializable
+data class TwitterMediaKt(
+ val id: Long,
+ val idStr: String,
+ val url: String,
+ val mediaUrl: String,
+ val mediaUrlHttps: String,
+ val expandedUrl: String,
+ val displayUrl: String,
+ val indices: List<Int>,
+ val type: String,
+ val sizes: SizeType,
+ val sourceStatusId: Long? = null,
+ val sourceStatusIdStr: String? = null
+)
+
+@Serializable
+data class TwitterUserMentionKt(
+ val screenName: String,
+ val name: String,
+ val id: Long,
+ val idStr: String,
+ val indices: List<Int>
+)
+
+@Serializable
+data class MetadataKt(
+ val resultType: String,
+ val isoLanguageCode: String
+)
+
+@Serializable
+data class TwitterTrimmedUserKt(
+ val id: Long,
+ val idStr: String,
+ val name: String,
+ val screenName: String,
+ val location: String,
+ val description: String,
+ val url: String?,
+ val entities: UserEntitiesKt,
+ val protected: Boolean,
+ val followersCount: Int,
+ val friendsCount: Int,
+ val listedCount: Int,
+ val createdAt: String,
+ val favouritesCount: Int,
+)
+
+@Serializable
+data class TwitterUserKt(
+ val id: Long,
+ val idStr: String,
+ val name: String,
+ val screenName: String,
+ val location: String,
+ val description: String,
+ val url: String?,
+ val entities: UserEntitiesKt,
+ val protected: Boolean,
+ val followersCount: Int,
+ val friendsCount: Int,
+ val listedCount: Int,
+ val createdAt: String,
+ val favouritesCount: Int,
+ val utcOffset: Int?,
+ val timeZone: String?,
+ val geoEnabled: Boolean,
+ val verified: Boolean,
+ val statusesCount: Int,
+ val lang: String,
+ val contributorsEnabled: Boolean,
+ val isTranslator: Boolean,
+ val isTranslationEnabled: Boolean,
+ val profileBackgroundColor: String,
+ val profileBackgroundImageUrl: String,
+ val profileBackgroundImageUrlHttps: String,
+ val profileBackgroundTile: Boolean,
+ val profileImageUrl: String,
+ val profileImageUrlHttps: String,
+ val profileBannerUrl: String? = null,
+ val profileLinkColor: String,
+ val profileSidebarBorderColor: String,
+ val profileSidebarFillColor: String,
+ val profileTextColor: String,
+ val profileUseBackgroundImage: Boolean,
+ val defaultProfile: Boolean,
+ val defaultProfileImage: Boolean,
+ val following: Boolean,
+ val followRequestSent: Boolean,
+ val notifications: Boolean
+)
+
+@Serializable
+data class UserEntitiesKt(
+ val url: Urls? = null,
+ val description: Urls
+)