avatarCodingFlower

Summary

The article discusses performance benchmarks for JSON serialization and deserialization in Kotlin using Jackson, Gson, and Kotlinx libraries.

Abstract

The article "How to increase your performance in Kotlin?" delves into the performance aspects of JSON serialization and deserialization, which are critical for applications handling large volumes of requests. It compares the performance of three popular JSON libraries: Jackson, Gson, and Kotlinx, using the Java Microbenchmark Harness (JMH) to conduct throughput tests on simple and complex JSON structures. The results indicate that Jackson generally outperforms in serialization tasks, while Gson excels in deserialization. Kotlinx, a library by JetBrains, shows mixed results, often falling in the middle. The article advises developers to choose a library based on their specific performance needs and code clarity preferences, suggesting Jackson for serialization and Gson for deserialization for optimal performance.

Opinions

  • The author suggests that default serialization and deserialization settings may not always be optimal and could lead to significant latency issues in applications handling millions of requests daily.
  • Jackson is praised for its high throughput in serialization tasks, making it a preferred choice for scenarios where serialization speed is crucial.
  • Gson is highlighted as the top performer in deserialization, particularly when dealing with complex JSON objects.
  • Kotlinx.serialization is acknowledged as a viable option but is not the leading performer in the benchmarks conducted.
  • The article emphasizes the importance of considering code clarity and maintainability, not just raw performance, when selecting a JSON library.
  • It is implied that combining Jackson for serialization and Gson for deserialization could yield the best performance outcomes.
  • The author recommends using widely adopted solutions like Jackson for their extensive support and battle-tested reliability, especially when performance gains are marginal.

How to increase your performance in Kotlin?

Have you ever wondered if your JSON serialization and deserialization performance is perfect? Are your defaults “optimal” in every scenario or maybe there is something superior?

We all know that whenever using REST, making calls to any other microservice or even working with some files, improvements in serialization and deserialization can be extremely quickly forgotten. However, they can create multiple problems, leading to some scenarios where handling 10,000,000 requests daily might create enormous latency in your application.

How to increase your performance in Kotlin? — Generated

Is there a way to measure it? If so — how? And what are maybe some alternatives in the typical serialization and deserialization scenario?

Before we dive into it — let’s see what tools are mostly used for this task.

Which JSON libraries are mostly used?

Jackson and Gson are mostly used, especially for Java, and are also similarly used for Kotlin on the backend. What is more, there was also kotlinx.serialization , a library released by JetBrains a couple of years ago. Having those three libraries, one might be more convenient to use Jackson, someone else prefers Gson and someone else native serialization.

Which JSON libraries are mostly used? — Generated

But is there one that is superior than the others?

How can you check performance in the system?

Usually — the tool that has been battle-tested and has been around for many years is Java Microbenchmark Harness (JMH) — which allows you to run nano/micro/milli/macro benchmarks which are written in languages that target JVM.

How can you check performance in the system? — Generated

Before working on any kind of performance test — it is important to answer a couple of questions:

What behaviour of the system do we plan to test?

We are verifying the serialization and deserialization of the JSON files.

What data will you use as input for tests?

In the current scenario, we have created four different JSON files:

  1. Simple JSON
{
  "website": "www.codingflower.com"
}

2. Complex JSON

{
  "id": "5fd2c8d36ce07686bcc76b2b",
  "index": 0,
  "guid": "25bbb5bd-4fcb-4dc5-ba93-f83816b4e502",
  "isActive": true,
  "picture": "http://placehold.it/32x32",
  "age": 25,
  "eyeColor": "blue",
  "name": {
    "first": "Shawna",
    "last": "Byers"
  },
  "company": "ZILLA",
  "tags": [
    "sit",
    "cillum",
    "irure",
    "deserunt",
    "elit",
    "mollit",
    "ad"
  ],
  "range": [
    0,
    1,
    8,
    9
  ],
  "friends": [
    {
      "id": 0,
      "name": "Hopkins Savage"
    },
    {
      "id": 1,
      "name": "Tasha Cooley"
    },
    {
      "id": 2,
      "name": "Tami Newman"
    },
    {
      "id": 3,
      "name": "Angelica Hopper"
    },
    {
      "id": 4,
      "name": "Valdez Terrell"
    },
    {
      "id": 5,
      "name": "Carlson Ramos"
    },
    {
      "id": 6,
      "name": "Elinor Pope"
    },
    {
      "id": 7,
      "name": "Vincent Rush"
    },
    {
      "id": 13,
      "name": "Elisa Burt"
    },
    {
      "id": 14,
      "name": "Maribel Jacobson"
    },
    {
      "id": 15,
      "name": "Blanche Fleming"
    },
    {
      "id": 16,
      "name": "Daisy Robles"
    },
    {
      "id": 17,
      "name": "Schultz Booker"
    }
  ],
  "rating": {
    "1": " Mcclain",
    "2": " Villarreal",
    "3": " Bailey",
    "4": " Keith",
    "5": " Michael",
    "6": " Booth",
    "14": " Joseph",
    "15": " Erickson",
    "16": " Willis",
    "17": " Carney",
    "18": " Hansen",
    "19": " Spencer",
    "20": " Le"
  }
}

3. List with 1000 simple JSONs

4. List with 100 complex JSONs

What implementations do you want to compare with each other?

We are trying to compare Jackson, Gson and Kotlinx.

What are you EXACTLY testing?

Here we are testing the number of operations per unit of time — Throughput.

How can you benchmark all of those tests?

Having all the above configuration we have 24 different runs — 3 libraries, 2 different behaviours and 4 different inputs, so let’s run it!

How can you benchmark all of those tests? — Generated

Simple JSON serialization

All three libraries had to serialize given simple objects to JSON.

@Benchmark
fun jackson(bl: Blackhole) {
    val text = jackson.writeValueAsString(simpleJson)
    bl.consume(text)
}

@Benchmark
fun gson(bl: Blackhole) {
    val text = gson.toJson(simpleJson)
    bl.consume(text)
}

@Benchmark
fun kotlin(bl: Blackhole) {
    val text = kotlinx.encodeToString(simpleJson)
    bl.consume(text)
}

The results are presented in the chart below.

Results of a simple object serialization

As you see, the highest throughput shows Jackson, followed by Gson and only afterwards Kotlinx. It gives a piece of short and concise information — for simple JSON, stick to Jackson!

Simple JSON deserialization

This time deserialization of simple JSON was measured:

@Benchmark
fun jackson(bl: Blackhole) {
    val simpleJson = jackson.readValue(simpleText, SimpleJson::class.java)
    bl.consume(simpleJson)
 }

@Benchmark
fun gson(bl: Blackhole) {
    val simpleJson = gson.fromJson(simpleText, SimpleJson::class.java)
    bl.consume(simpleJson)
}

@Benchmark
fun kotlinx(bl: Blackhole) {
    val simpleJson = kotlinx.decodeFromString<SimpleJson>(simpleText)
    bl.consume(simpleJson)
}

And the results are as follows:

Results of a simple object deserialization

As you see, there isn’t a huge difference between Kotlinx and Jackson as it was before. What is more — Gson is leading in this scenario for simple deserialization of the objects.

Simple objects list serialization

Similarly to the scenario above — we have verified with the simple objects list situation. As the code looks extremely similar, here is the most important thing, the results:

Results of a simple object list serialization

Jackson is still leading with the serialization. On the other hand, Kotlinx has taken over the 2nd place over the Gson and stopped being the last place.

Simple JSON list deserialization

The chart with the result of benchmarking 1000 simpleJson in one JSON array is under the code snippet:

@Benchmark
fun jackson(bl: Blackhole) {
    val simpleJsons = jackson.readValue<List<SimpleJson>>(text)
    bl.consume(simpleJsons)
}

@Benchmark
fun gson(bl: Blackhole) {
    val itemType = object : TypeToken<List<SimpleJson>>() {}.type
    val simpleJsons = gson.fromJson<List<SimpleJson>>(text, itemType)
    bl.consume(simpleJsons)
}

@Benchmark
fun kotlinx(bl: Blackhole) {
    val simpleJsons = kotlinx.decodeFromString<List<SimpleJson>>(text)
    bl.consume(simpleJsons)
}

The results are as follows:

Results of a simple object list deserialization

And similarly with the deserialization of the simple objects — Gson is the fastest. Kotlinx also took over Jackson in 2nd place however, still didn't manage to beat any other library in any of the scenarios.

Complex JSON serialization and deserialization

Similarly performed, here are the checks for the complex JSON verification.

Results of a complex json performance serialization
Results of a complex json performance deserialization

As you see, the leader in object serialization is mostly Jackson. On the other hand, deserialization seems to be winning almost always Gson. Kotlinx is never in the first place.

Complex objects list serialization and deserialization

Serializing the list of 100 complex objects shows similar results to the one above:

Results of a complex json list performance serialization
Results of a complex json list performance deserialization

Jackson is leading the serialization throughput, yet the deserialization is working much better for Gson.

Which should you choose?

There are a couple of things important here — sometimes what you need is not performance, but clarity in the code, therefore always consider whether the changes that you are going to make will not make your code more ugly than it already is.

Which should you choose? — Generated

On the other hand , if you look to improve your performance, based on those results, we can see a couple of things:

  1. Jackson is fastest in serialization, but also slowest in deserialization
  2. Gson copes best with deserialization, but complex objects are much harder for him
  3. Kotlinx is kind of in the middle — not the worst, but also not the best

If you are looking for the fastest solution — Jackson for serialization and Gson for deserialization. Combining benefits of both of them, gives you the performance and power that you need.

On the other hand, if you aren’t looking for a tenth of a second improvement in JSON serialization and deserialization, go for something that is the most widely used, like Jackson, making sure that you can easily achieve everything there and that this solution is extremely battle-tested.

If you are looking for some other stories related to Java or Kotlin, check out this:

Technology
Programming
Json
Kotlin
Software Development
Recommended from ReadMedium