Skip to content

Commit 363c6de

Browse files
Copilothossain-khan
andcommitted
Add external JSON5 library for three-way benchmark comparison
Co-authored-by: hossain-khan <[email protected]>
1 parent 9df5614 commit 363c6de

File tree

5 files changed

+169
-17
lines changed

5 files changed

+169
-17
lines changed

benchmark/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ dependencies {
1111

1212
// kotlinx.serialization for JSON comparison
1313
implementation(libs.bundles.kotlinxEcosystem)
14+
15+
// External JSON5 library for comparison
16+
implementation("at.syntaxerror:json5:2.1.0")
1417

1518
// Test dependencies
1619
testImplementation(kotlin("test"))

benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/BenchmarkRunner.kt

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,35 @@ object BenchmarkRunner {
117117

118118
val json5Result = resultList.find { it.format == "JSON5" }
119119
val jsonResult = resultList.find { it.format == "JSON" }
120+
val externalJson5Result = resultList.find { it.format == "External-JSON5" }
120121

121-
if (json5Result != null && jsonResult != null) {
122-
appendLine(" JSON5: ${String.format("%.3f", json5Result.averageTimeMillis)} ms avg")
123-
appendLine(" JSON: ${String.format("%.3f", jsonResult.averageTimeMillis)} ms avg")
122+
if (json5Result != null && jsonResult != null && externalJson5Result != null) {
123+
appendLine(" JSON5: ${String.format("%.3f", json5Result.averageTimeMillis)} ms avg")
124+
appendLine(" JSON: ${String.format("%.3f", jsonResult.averageTimeMillis)} ms avg")
125+
appendLine(" External-JSON5: ${String.format("%.3f", externalJson5Result.averageTimeMillis)} ms avg")
124126

125-
val speedup = json5Result.averageTimeMillis / jsonResult.averageTimeMillis
126-
if (speedup > 1.0) {
127-
appendLine(" → JSON is ${String.format("%.2f", speedup)}x faster than JSON5")
127+
// Compare all libraries
128+
val json5VsJson = json5Result.averageTimeMillis / jsonResult.averageTimeMillis
129+
val externalVsJson = externalJson5Result.averageTimeMillis / jsonResult.averageTimeMillis
130+
val json5VsExternal = json5Result.averageTimeMillis / externalJson5Result.averageTimeMillis
131+
132+
appendLine(" Comparisons:")
133+
if (json5VsJson > 1.0) {
134+
appendLine(" → JSON is ${String.format("%.2f", json5VsJson)}x faster than JSON5")
135+
} else {
136+
appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / json5VsJson)}x faster than JSON")
137+
}
138+
139+
if (externalVsJson > 1.0) {
140+
appendLine(" → JSON is ${String.format("%.2f", externalVsJson)}x faster than External-JSON5")
141+
} else {
142+
appendLine(" → External-JSON5 is ${String.format("%.2f", 1.0 / externalVsJson)}x faster than JSON")
143+
}
144+
145+
if (json5VsExternal > 1.0) {
146+
appendLine(" → External-JSON5 is ${String.format("%.2f", json5VsExternal)}x faster than JSON5")
128147
} else {
129-
appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / speedup)}x faster than JSON")
148+
appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / json5VsExternal)}x faster than External-JSON5")
130149
}
131150
}
132151
appendLine()
@@ -136,18 +155,37 @@ object BenchmarkRunner {
136155
appendLine("Overall Statistics:")
137156
val json5Results = results.filter { it.format == "JSON5" }
138157
val jsonResults = results.filter { it.format == "JSON" }
158+
val externalJson5Results = results.filter { it.format == "External-JSON5" }
139159

140160
val avgJson5Time = json5Results.map { it.averageTimeMillis }.average()
141161
val avgJsonTime = jsonResults.map { it.averageTimeMillis }.average()
162+
val avgExternalJson5Time = externalJson5Results.map { it.averageTimeMillis }.average()
142163

143-
appendLine("Average JSON5 time: ${String.format("%.3f", avgJson5Time)} ms")
144-
appendLine("Average JSON time: ${String.format("%.3f", avgJsonTime)} ms")
164+
appendLine("Average JSON5 time: ${String.format("%.3f", avgJson5Time)} ms")
165+
appendLine("Average JSON time: ${String.format("%.3f", avgJsonTime)} ms")
166+
appendLine("Average External-JSON5 time: ${String.format("%.3f", avgExternalJson5Time)} ms")
167+
168+
val json5VsJson = avgJson5Time / avgJsonTime
169+
val externalVsJson = avgExternalJson5Time / avgJsonTime
170+
val json5VsExternal = avgJson5Time / avgExternalJson5Time
171+
172+
appendLine("\nOverall Comparisons:")
173+
if (json5VsJson > 1.0) {
174+
appendLine(" → JSON is ${String.format("%.2f", json5VsJson)}x faster than JSON5")
175+
} else {
176+
appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / json5VsJson)}x faster than JSON")
177+
}
178+
179+
if (externalVsJson > 1.0) {
180+
appendLine(" → JSON is ${String.format("%.2f", externalVsJson)}x faster than External-JSON5")
181+
} else {
182+
appendLine(" → External-JSON5 is ${String.format("%.2f", 1.0 / externalVsJson)}x faster than JSON")
183+
}
145184

146-
val overallSpeedup = avgJson5Time / avgJsonTime
147-
if (overallSpeedup > 1.0) {
148-
appendLine("Overall: JSON is ${String.format("%.2f", overallSpeedup)}x faster than JSON5")
185+
if (json5VsExternal > 1.0) {
186+
appendLine(" → External-JSON5 is ${String.format("%.2f", json5VsExternal)}x faster than JSON5")
149187
} else {
150-
appendLine("Overall: JSON5 is ${String.format("%.2f", 1.0 / overallSpeedup)}x faster than JSON")
188+
appendLine("JSON5 is ${String.format("%.2f", 1.0 / json5VsExternal)}x faster than External-JSON5")
151189
}
152190
}
153191

@@ -161,9 +199,10 @@ object BenchmarkRunner {
161199
val (dataType, operation) = key.split("_")
162200
val json5Result = resultList.find { it.format == "JSON5" }
163201
val jsonResult = resultList.find { it.format == "JSON" }
202+
val externalJson5Result = resultList.find { it.format == "External-JSON5" }
164203

165-
if (json5Result != null && jsonResult != null) {
166-
println("$dataType $operation: JSON5=${String.format("%.3f", json5Result.averageTimeMillis)}ms, JSON=${String.format("%.3f", jsonResult.averageTimeMillis)}ms")
204+
if (json5Result != null && jsonResult != null && externalJson5Result != null) {
205+
println("$dataType $operation: JSON5=${String.format("%.3f", json5Result.averageTimeMillis)}ms, JSON=${String.format("%.3f", jsonResult.averageTimeMillis)}ms, External-JSON5=${String.format("%.3f", externalJson5Result.averageTimeMillis)}ms")
167206
}
168207
}
169208
}

benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/SerializationBenchmark.kt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import kotlinx.serialization.encodeToString
66
import kotlinx.serialization.decodeFromString
77
import kotlinx.serialization.serializer
88
import kotlin.system.measureNanoTime
9+
import at.syntaxerror.json5.*
910

1011
/**
1112
* Benchmark class for comparing JSON5 and standard JSON performance.
@@ -38,6 +39,35 @@ class SerializationBenchmark {
3839
}
3940
}
4041

42+
/**
43+
* Converts a kotlinx.serialization object to a plain object that can be used with the external JSON5 library.
44+
* This is done by serializing to JSON with kotlinx.serialization and then parsing with external JSON5 library.
45+
*/
46+
private fun <T> convertToPlainObject(data: T, serializer: kotlinx.serialization.SerializationStrategy<T>): Any? {
47+
val jsonString = json.encodeToString(serializer, data)
48+
val parser = JSONParser(jsonString)
49+
return parser.nextValue()
50+
}
51+
52+
/**
53+
* Converts a JSON5 string to a plain object using the external JSON5 library.
54+
*/
55+
private fun parseWithExternalLibrary(json5String: String): Any? {
56+
val parser = JSONParser(json5String)
57+
return parser.nextValue()
58+
}
59+
60+
/**
61+
* Converts a plain object to JSON5 string using the external JSON5 library.
62+
*/
63+
private fun stringifyWithExternalLibrary(obj: Any?): String {
64+
return when (obj) {
65+
is JSONObject -> JSONStringify.toString(obj, 0)
66+
is JSONArray -> JSONStringify.toString(obj, 0)
67+
else -> obj?.toString() ?: "null"
68+
}
69+
}
70+
4171
/**
4272
* Runs serialization benchmark for the given data and serializer.
4373
*/
@@ -89,6 +119,27 @@ class SerializationBenchmark {
89119
)
90120
)
91121

122+
// Benchmark external JSON5 library serialization
123+
val externalJson5Object = convertToPlainObject(data, serializer)
124+
val externalJson5SerializationTime = measureNanoTime {
125+
repeat(iterations) {
126+
stringifyWithExternalLibrary(externalJson5Object)
127+
}
128+
}
129+
130+
results.add(
131+
BenchmarkResult(
132+
operation = "Serialization",
133+
dataType = dataTypeName,
134+
format = "External-JSON5",
135+
iterations = iterations,
136+
totalTimeNanos = externalJson5SerializationTime,
137+
averageTimeNanos = externalJson5SerializationTime / iterations,
138+
averageTimeMicros = (externalJson5SerializationTime / iterations) / 1000.0,
139+
averageTimeMillis = (externalJson5SerializationTime / iterations) / 1_000_000.0
140+
)
141+
)
142+
92143
return results
93144
}
94145

@@ -144,6 +195,26 @@ class SerializationBenchmark {
144195
)
145196
)
146197

198+
// Benchmark external JSON5 library deserialization
199+
val externalJson5DeserializationTime = measureNanoTime {
200+
repeat(iterations) {
201+
parseWithExternalLibrary(json5String)
202+
}
203+
}
204+
205+
results.add(
206+
BenchmarkResult(
207+
operation = "Deserialization",
208+
dataType = dataTypeName,
209+
format = "External-JSON5",
210+
iterations = iterations,
211+
totalTimeNanos = externalJson5DeserializationTime,
212+
averageTimeNanos = externalJson5DeserializationTime / iterations,
213+
averageTimeMicros = (externalJson5DeserializationTime / iterations) / 1000.0,
214+
averageTimeMillis = (externalJson5DeserializationTime / iterations) / 1_000_000.0
215+
)
216+
)
217+
147218
return results
148219
}
149220

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dev.hossain.json5kt.benchmark.test
2+
3+
import at.syntaxerror.json5.*
4+
5+
fun main() {
6+
println("Testing external JSON5 library API...")
7+
8+
try {
9+
val testJson = "{name: 'John', age: 30}"
10+
11+
// Try JSONParser
12+
try {
13+
// Parse JSON5
14+
val parser = JSONParser(testJson)
15+
val parsed = parser.nextValue()
16+
println("Parsed with JSONParser: $parsed")
17+
18+
// Create JSONObject and stringify
19+
val testObj = JSONObject()
20+
testObj.set("name", "Jane")
21+
testObj.set("age", 25)
22+
23+
val stringified = JSONStringify.toString(testObj, 0)
24+
println("Stringified with JSONStringify: $stringified")
25+
26+
} catch (e: Exception) {
27+
println("JSONParser/JSONStringify approach failed: ${e.message}")
28+
e.printStackTrace()
29+
}
30+
31+
} catch (e: Exception) {
32+
println("Error: ${e.message}")
33+
e.printStackTrace()
34+
}
35+
}

benchmark/src/test/kotlin/dev/hossain/json5kt/benchmark/BenchmarkTest.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,22 @@ class BenchmarkTest {
4545
assertNotNull(results)
4646
assertTrue(results.isNotEmpty())
4747

48-
// Should have 4 results: JSON5 and JSON for both serialization and deserialization
49-
assertEquals(4, results.size)
48+
// Should have 6 results: JSON5, JSON, and External-JSON5 for both serialization and deserialization
49+
assertEquals(6, results.size)
5050

5151
val json5SerializationResult = results.find { it.format == "JSON5" && it.operation == "Serialization" }
5252
val jsonSerializationResult = results.find { it.format == "JSON" && it.operation == "Serialization" }
53+
val externalJson5SerializationResult = results.find { it.format == "External-JSON5" && it.operation == "Serialization" }
5354
val json5DeserializationResult = results.find { it.format == "JSON5" && it.operation == "Deserialization" }
5455
val jsonDeserializationResult = results.find { it.format == "JSON" && it.operation == "Deserialization" }
56+
val externalJson5DeserializationResult = results.find { it.format == "External-JSON5" && it.operation == "Deserialization" }
5557

5658
assertNotNull(json5SerializationResult)
5759
assertNotNull(jsonSerializationResult)
60+
assertNotNull(externalJson5SerializationResult)
5861
assertNotNull(json5DeserializationResult)
5962
assertNotNull(jsonDeserializationResult)
63+
assertNotNull(externalJson5DeserializationResult)
6064

6165
// Verify basic properties
6266
assertEquals("TestPerson", json5SerializationResult!!.dataType)

0 commit comments

Comments
 (0)