1 package kotlinx.benchmarks.json 2 3 import kotlinx.benchmarks.model.* 4 import kotlinx.serialization.* 5 import kotlinx.serialization.json.* 6 import org.openjdk.jmh.annotations.* 7 import java.util.concurrent.* 8 9 @Warmup(iterations = 7, time = 1) 10 @Measurement(iterations = 7, time = 1) 11 @BenchmarkMode(Mode.Throughput) 12 @OutputTimeUnit(TimeUnit.SECONDS) 13 @State(Scope.Benchmark) 14 @Fork(2) 15 open class TwitterFeedBenchmark { 16 17 /* 18 * Macro feed benchmark with a lot of UTF-16 used to track general regressions. 19 * 20 * This is a small piece of twitter feed taken from one of the simdjson repository 21 * with Kotlin classes generated by Json2Kotlin plugin (and also manually adjusted) 22 */ 23 private val input = TwitterFeedBenchmark::class.java.getResource("/twitter_macro.json").readBytes().decodeToString() 24 private val twitter = Json.decodeFromString(MacroTwitterFeed.serializer(), input) 25 <lambda>null26 private val jsonNoAltNames = Json { useAlternativeNames = false } <lambda>null27 private val jsonIgnoreUnknwn = Json { ignoreUnknownKeys = true } <lambda>null28 private val jsonIgnoreUnknwnNoAltNames = Json { ignoreUnknownKeys = true; useAlternativeNames = false } <lambda>null29 private val jsonNamingStrategy = Json { namingStrategy = JsonNamingStrategy.SnakeCase } <lambda>null30 private val jsonNamingStrategyIgnoreUnknwn = Json(jsonNamingStrategy) { ignoreUnknownKeys = true } 31 32 private val twitterKt = jsonNamingStrategy.decodeFromString(MacroTwitterFeedKt.serializer(), input) 33 34 @Setup initnull35 fun init() { 36 require(twitter == Json.decodeFromString(MacroTwitterFeed.serializer(), Json.encodeToString(MacroTwitterFeed.serializer(), twitter))) 37 } 38 39 // Order of magnitude: ~500 op/s 40 @Benchmark decodeTwitternull41 fun decodeTwitter() = Json.decodeFromString(MacroTwitterFeed.serializer(), input) 42 43 // Should be the same as decodeTwitter, since decodeTwitter never hit unknown name and therefore should never build deserializationNamesMap anyway 44 @Benchmark 45 fun decodeTwitterNoAltNames() = jsonNoAltNames.decodeFromString(MacroTwitterFeed.serializer(), input) 46 47 @Benchmark 48 fun encodeTwitter() = Json.encodeToString(MacroTwitterFeed.serializer(), twitter) 49 50 @Benchmark 51 fun decodeMicroTwitter() = jsonIgnoreUnknwn.decodeFromString(MicroTwitterFeed.serializer(), input) 52 53 // Should be faster than decodeMicroTwitter, as we explicitly opt-out from deserializationNamesMap on unknown name 54 @Benchmark 55 fun decodeMicroTwitterNoAltNames() = jsonIgnoreUnknwnNoAltNames.decodeFromString(MicroTwitterFeed.serializer(), input) 56 57 // Should be just a bit slower than decodeMicroTwitter, because alternative names map is created in both cases 58 @Benchmark 59 fun decodeMicroTwitterWithNamingStrategy(): MicroTwitterFeedKt = jsonNamingStrategyIgnoreUnknwn.decodeFromString(MicroTwitterFeedKt.serializer(), input) 60 61 // Can be slower than decodeTwitter, as we always build deserializationNamesMap when naming strategy is used 62 @Benchmark 63 fun decodeTwitterWithNamingStrategy(): MacroTwitterFeedKt = jsonNamingStrategy.decodeFromString(MacroTwitterFeedKt.serializer(), input) 64 65 // 15-20% slower than without the strategy. Without serializationNamesMap (invoking strategy on every write), up to 50% slower 66 @Benchmark 67 fun encodeTwitterWithNamingStrategy(): String = jsonNamingStrategy.encodeToString(MacroTwitterFeedKt.serializer(), twitterKt) 68 69 } 70