Skip to content

Commit a0be5ed

Browse files
committed
Introduce the basis Moshi benchmark setup in order to use it for further comparisons
Relates to #2657
1 parent 6886e34 commit a0be5ed

File tree

5 files changed

+155
-52
lines changed

5 files changed

+155
-52
lines changed

benchmark/build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ plugins {
1212
alias(libs.plugins.serialization)
1313
alias(libs.plugins.shadow)
1414
alias(libs.plugins.jmh)
15+
kotlin("kapt") // For annotation processing
1516
}
1617

1718
java {
@@ -64,4 +65,8 @@ dependencies {
6465
implementation(project(":kotlinx-serialization-json-okio"))
6566
implementation(project(":kotlinx-serialization-json-io"))
6667
implementation(project(":kotlinx-serialization-protobuf"))
68+
69+
// Moshi
70+
implementation(libs.moshi.kotlin)
71+
kapt(libs.moshi.codegen)
6772
}

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

+8-52
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kotlinx.benchmarks.json
22

33
import com.fasterxml.jackson.databind.*
44
import com.fasterxml.jackson.module.kotlin.*
5+
import benchmarks.model.*
56
import kotlinx.serialization.*
67
import kotlinx.serialization.json.*
78
import kotlinx.serialization.json.okio.encodeToBufferedSink
@@ -20,53 +21,8 @@ import java.util.concurrent.*
2021
@Fork(2)
2122
open class JacksonComparisonBenchmark {
2223

23-
@Serializable
24-
data class DefaultPixelEvent(
25-
val version: Int,
26-
val dateTime2: String,
27-
val serverName: String,
28-
val domain: String,
29-
val method: String,
30-
val clientIp: String,
31-
val queryString: String,
32-
val userAgent: String,
33-
val contentType: String,
34-
val browserLanguage: String,
35-
val postData: String,
36-
val cookies: String
37-
)
38-
3924
private val objectMapper: ObjectMapper = jacksonObjectMapper()
4025

41-
private val data = DefaultPixelEvent(
42-
version = 1,
43-
dateTime2 = System.currentTimeMillis().toString(),
44-
serverName = "some-endpoint-qwer",
45-
domain = "some.domain.com",
46-
method = "POST",
47-
clientIp = "127.0.0.1",
48-
queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=FoolbarActive&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=66abafd0d49f42e58dc7536109395306&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
49-
userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
50-
contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
51-
browserLanguage = "en-US,en;q=0.5",
52-
postData = "-",
53-
cookies = "_ga=GA1.2.971852807.1546968515"
54-
)
55-
56-
private val dataWithEscapes = DefaultPixelEvent(
57-
version = 1,
58-
dateTime2 = System.currentTimeMillis().toString(),
59-
serverName = "some-endp\"oint-qwer",
60-
domain = "<a href=\"some.domain.com\">",
61-
method = "POST",
62-
clientIp = "127.0.0.1",
63-
queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=\"FoolbarActive\"&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=\"66abafd0d49f42e58dc7536109395306\"&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
64-
userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
65-
contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
66-
browserLanguage = "\"en\"-\"US\",en;\\q=0.5",
67-
postData = "-",
68-
cookies = "_ga=GA1.2.971852807.1546968515"
69-
)
7026

7127
private val devNullSink = blackholeSink().buffer()
7228
private val devNullStream = object : OutputStream() {
@@ -75,7 +31,7 @@ open class JacksonComparisonBenchmark {
7531
override fun write(b: ByteArray, off: Int, len: Int) {}
7632
}
7733

78-
private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), data)
34+
private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
7935
private val utf8BytesData = stringData.toByteArray()
8036

8137
@Serializable
@@ -84,28 +40,28 @@ open class JacksonComparisonBenchmark {
8440
private val smallData = SmallDataClass(42, "Vincent")
8541

8642
@Benchmark
87-
fun jacksonToString(): String = objectMapper.writeValueAsString(data)
43+
fun jacksonToString(): String = objectMapper.writeValueAsString(pixelEvent)
8844

8945
@Benchmark
90-
fun jacksonToStringWithEscapes(): String = objectMapper.writeValueAsString(dataWithEscapes)
46+
fun jacksonToStringWithEscapes(): String = objectMapper.writeValueAsString(pixelEventWithEscapes)
9147

9248
@Benchmark
9349
fun jacksonSmallToString(): String = objectMapper.writeValueAsString(smallData)
9450

9551
@Benchmark
96-
fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), data)
52+
fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
9753

9854
@Benchmark
99-
fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), data, devNullStream)
55+
fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), pixelEvent, devNullStream)
10056

10157
@Benchmark
10258
fun kotlinFromStream() = Json.decodeFromStream(DefaultPixelEvent.serializer(), ByteArrayInputStream(utf8BytesData))
10359

10460
@Benchmark
105-
fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), data, devNullSink)
61+
fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), pixelEvent, devNullSink)
10662

10763
@Benchmark
108-
fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), dataWithEscapes)
64+
fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEventWithEscapes)
10965

11066
@Benchmark
11167
fun kotlinSmallToString(): String = Json.encodeToString(SmallDataClass.serializer(), smallData)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.benchmarks.json.moshi
6+
7+
8+
import com.squareup.moshi.*
9+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
10+
import benchmarks.model.*import kotlinx.serialization.json.*
11+
import kotlinx.serialization.json.Json
12+
import kotlinx.serialization.json.okio.*
13+
import okio.*
14+
import org.openjdk.jmh.annotations.*
15+
import java.io.*
16+
import java.util.concurrent.*
17+
18+
19+
/**
20+
* Note: these benchmarks were not checked to compare anything meaningful.
21+
* It's just a baseline to simplify Moshi configuration for a more intricated comparisons.
22+
* // M3, 1.7.1, Corretto 17.0.7
23+
*
24+
* Benchmark Mode Cnt Score Error Units
25+
* MoshiBaseline.kotlinFromSource thrpt 5 283.587 ± 10.556 ops/ms
26+
* MoshiBaseline.kotlinFromString thrpt 5 1518.012 ± 47.191 ops/ms
27+
* MoshiBaseline.kotlinToOkio thrpt 5 1055.492 ± 48.782 ops/ms
28+
* MoshiBaseline.kotlinToString thrpt 5 2264.407 ± 88.324 ops/ms
29+
*
30+
* MoshiBaseline.moshiFromSource thrpt 5 1280.841 ± 81.180 ops/ms
31+
* MoshiBaseline.moshiFromString thrpt 5 1137.416 ± 63.516 ops/ms
32+
* MoshiBaseline.moshiToOkio thrpt 5 963.130 ± 50.316 ops/ms
33+
* MoshiBaseline.moshiToString thrpt 5 1061.251 ± 10.217 ops/ms
34+
*/
35+
@Warmup(iterations = 5, time = 1)
36+
@Measurement(iterations = 5, time = 1)
37+
@BenchmarkMode(Mode.Throughput)
38+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
39+
@State(Scope.Benchmark)
40+
@Fork(1)
41+
open class MoshiBaseline {
42+
43+
private val moshi = Moshi.Builder()
44+
.add(KotlinJsonAdapterFactory())
45+
.build()
46+
47+
private val jsonAdapter = moshi.adapter(DefaultPixelEvent::class.java)
48+
49+
private val devNullSink = blackholeSink().buffer()
50+
51+
private val source = Buffer().writeUtf8(pixelEventJson)
52+
53+
// Moshi
54+
55+
@Benchmark
56+
fun moshiToString(): String = jsonAdapter.toJson(pixelEvent)
57+
58+
@Benchmark
59+
fun moshiToOkio() = jsonAdapter.toJson(devNullSink, pixelEvent)
60+
61+
@Benchmark
62+
fun moshiFromString(): DefaultPixelEvent = jsonAdapter.fromJson(pixelEventJson)!!
63+
64+
@Benchmark
65+
fun moshiFromSource(): DefaultPixelEvent = jsonAdapter.fromJson(source.copy())!!
66+
67+
// Kx
68+
69+
@Benchmark
70+
fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
71+
72+
@Benchmark
73+
fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), pixelEvent, devNullSink)
74+
75+
@Benchmark
76+
fun kotlinFromString(): DefaultPixelEvent = Json.decodeFromString(DefaultPixelEvent.serializer(), pixelEventJson)
77+
78+
@Benchmark
79+
fun kotlinFromSource(): DefaultPixelEvent = Json.decodeFromBufferedSource(DefaultPixelEvent.serializer(), source.copy())
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package benchmarks.model // NOT A KOTLINX PACKAGE. Otherwise Moshi starts complaining on platform classes
6+
7+
import kotlinx.serialization.*
8+
import kotlinx.serialization.json.*
9+
10+
// For Jackson/Moshi data
11+
12+
13+
@Serializable
14+
data class DefaultPixelEvent(
15+
val version: Int,
16+
val dateTime2: String,
17+
val serverName: String,
18+
val domain: String,
19+
val method: String,
20+
val clientIp: String,
21+
val queryString: String,
22+
val userAgent: String,
23+
val contentType: String,
24+
val browserLanguage: String,
25+
val postData: String,
26+
val cookies: String
27+
)
28+
29+
val pixelEvent = DefaultPixelEvent(
30+
version = 1,
31+
dateTime2 = System.currentTimeMillis().toString(),
32+
serverName = "some-endpoint-qwer",
33+
domain = "some.domain.com",
34+
method = "POST",
35+
clientIp = "127.0.0.1",
36+
queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=FoolbarActive&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=66abafd0d49f42e58dc7536109395306&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
37+
userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
38+
contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
39+
browserLanguage = "en-US,en;q=0.5",
40+
postData = "-",
41+
cookies = "_ga=GA1.2.971852807.1546968515"
42+
)
43+
44+
val pixelEventJson = Json.encodeToString(pixelEvent)
45+
46+
val pixelEventWithEscapes = DefaultPixelEvent(
47+
version = 1,
48+
dateTime2 = System.currentTimeMillis().toString(),
49+
serverName = "some-endp\"oint-qwer",
50+
domain = "<a href=\"some.domain.com\">",
51+
method = "POST",
52+
clientIp = "127.0.0.1",
53+
queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=\"FoolbarActive\"&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=\"66abafd0d49f42e58dc7536109395306\"&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
54+
userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
55+
contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
56+
browserLanguage = "\"en\"-\"US\",en;\\q=0.5",
57+
postData = "-",
58+
cookies = "_ga=GA1.2.971852807.1546968515"
59+
)

gradle/libs.versions.toml

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jackson = "2.13.3"
1515
okio = "3.9.0"
1616
kotlinx-io="0.4.0"
1717
gson = "2.8.5"
18+
moshi = "1.15.1"
1819
kotlintest = "2.0.7"
1920
coroutines = "1.6.4"
2021
cbor = "4.2.0"
@@ -48,6 +49,8 @@ typesafe-config = { module = "com.typesafe:config", version.ref = "typesafe-conf
4849
junit-junit4 = { module = "junit:junit", version.ref = "junit4"}
4950
protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc"}
5051
protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protoc" }
52+
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
53+
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
5154

5255
[plugins]
5356
knit = { id = "org.jetbrains.kotlinx.knit", version.ref = "knit" }

0 commit comments

Comments
 (0)