Moshi vs KotlinX Serialization — the ultimate benchmark

Kacper Wojciechowski
6 min readJul 6, 2023

--

Photo by Chris Liverani on Unsplash

Moshi and KotlinX Serialization are solutions commonly used in Kotlin language to work with data in JSON format. I’ve decided to look at their performance — as performance is usually one of the main arguments when you have to choose one of them.

Without beating about the bush — let's dive straight into the topic!

Prequisitions and methodology

Here are the versions of the used tools:

  • Kotlin 1.8.22
  • Moshi 1.15.0
  • KotlinX Serialization 1.5.1

The benchmark consists of 3 different use cases. The data is read from the JSON file as a string. Then we measure 3 different factors:

  1. Serializer/adapter creation time (a commonly overlooked factor that is surprisingly significant in the conversation)
  2. Deserialization (string -> model)
  3. Serialization (model -> string)

This process is repeated 3 times to check how the used tool utilizes caching mechanisms. The 2 tools are benchmarked in 2 separate runs, as it seems like running both of them next to each other affects the performance measurements. Probably both tools share some solutions that have their own caching mechanisms, so if one has finished — the second one has some work already done.

The output format (in milliseconds):

  • Serializer/adapter creation times — creation time of serializer/adapter
  • Deserialization times — deserialization time (string -> model)
  • Serialization times — serialization time (model -> string)
  • Combined deserialization times — the sum of serializer/adapter creation time and deserialization
  • Combined serialization times — the sum of serializer/adapter creation time and serialization

The code used for this benchmark can be found on my public GitHub repository. If my article isn’t enough, feel free to play with it to find the benchmark you’re looking for.

Simple standard model

The first case is a simple model that does not contain any polymorphic classes. The JSON data contains exactly 1 object, so we’re not touching the generics yet.

https://github.com/buszi0809/serialization-benchmark/blob/main/src/main/kotlin/StandardModel.kt

And here we have the benchmark results:

Moshi
Adapter creation times: [111.09, 0.01, 0.0 ]
Deserialization times: [ 5.62, 0.21, 0.2 ]
Serialization times: [ 0.16, 0.09, 0.09]
Combined deserialization times: [116.71, 0.22, 0.2 ]
Combined serialization times: [111.25, 0.1, 0.09]
KotlinX
Serializer creation times: [ 13.86, 0.0, 0.0 ]
Deserialization times: [ 30.4, 0.12, 0.13]
Serialization times: [ 9.51, 0.11, 0.17]
Combined deserialization times: [ 44.26, 0.12, 0.13]
Combined serialization times: [ 23.37, 0.11, 0.17]

Moshi seems to be initializing the adapter much slower in the first iteration, but serializing/deserializing is significantly faster. When the caching kicks in — both solutions start to work in a matter of microseconds.

To uncover the true story we need to look at the sum of both serialization/deserialization and adapter/serializer creation time. The full process seems to be many times faster with the use of KotlinX Serialization.

Moshi 0 : 1 KotlinX

Large list

The second case is a large list of standard models. The JSON file weighs 26MB (credits to this repo) so it should extensively test our tools when it comes to the work with huge datasets. We are also entering the generics world as we’re using a list. It extends the work to be done when creating an adapter/serializer as it requires not only knowing how to work with the actual model but also needs to know how to work with a list.

https://github.com/buszi0809/serialization-benchmark/blob/main/src/main/kotlin/LargeList.kt

And here we have the benchmark result :

Moshi
Adapter creation times: [124.79, 0.31, 0.24]
Deserialization times: [416.58, 184.15, 95.4 ]
Serialization times: [136.78, 57.7, 34.87]
Combined deserialization times: [541.37, 184.46, 95.64]
Combined serialization times: [261.57, 58.01, 35.11]
KotlinX
Serializer creation times: [ 18.82, 0.02, 0.01 ]
Deserialization times: [328.83, 105.78, 82.14]
Serialization times: [130.61, 47.93, 30.9 ]
Combined deserialization times: [347.65, 105.8, 82.15]
Combined serialization times: [149.43, 47.95, 30.91]

Now it seems that KotlinX really starts to shine. Not only serializer creation time is almost 7 times faster, but it also seems to be winning in the field of deserialization/serialization. We start to see a pattern here. 🤔

Moshi 0 : 2 KotlinX

Sealed classes

Sealed classes are a big topic in the world of Kotlin. They are handy to use in serialization as they let us use different models in the same field in a really easy, cohesive, and readable way. Let’s have a look at how both tools behave with such key language feature.

https://github.com/buszi0809/serialization-benchmark/blob/main/src/main/kotlin/SealedClass.kt

And here we’ve hit, in my opinion, the 2 biggest bottlenecks of Moshi:

  1. Moshi does not support Kotlin objects. The serializable needs to be a class. Otherwise, it will fail to generate the adapter during the build time. KotlinX supports objects out of the box.
  2. In order for sealed classes to work, you need to define their behavior when creating a Moshi instance. It is actually a big deal, as you need to somehow overcome the issue when the model is in a different module than the place where you instantiate Moshi. KotlinX does not require such a setup, as it accepts the definition of behavior via annotations on the actual model.

I am aware that there is a third-party library that fixes those issues, but the third party is a third party and in my opinion, it does not explain why Moshi does not support those out of the box.

But let’s get back to the benchmark results:

Moshi
Adapter creation times: [131.02, 0.35, 0.14]
Deserialization times: [ 20.22, 0.72, 0.52]
Serialization times: [ 0.17, 0.13, 0.25]
Combined deserialization times: [151.24, 1.07, 0.66]
Combined serialization times: [131.19, 0.48, 0.39]
KotlinX
Serializer creation times: [ 77.33, 0.01, 0.0 ]
Deserialization times: [ 19.43, 0.2, 0.22]
Serialization times: [ 15.55, 0.21, 0.16]
Combined deserialization times: [ 96.76, 0.21, 0.22]
Combined serialization times: [ 92.88, 0.22, 0.16]

Seems like KotlinX not only wins in the field of convenience but also in the field of speed. I think we all have a clear winner here.

Moshi 0 : 3 KotlinX

Conclusion

It seems like KotlinX is just … better. It is much faster, convenient, and just much more “kotlinish”. It also has much faster support for the new Kotlin versions. Many times I wanted to bump the Kotlin version, but just couldn’t, because Moshi did not support it yet. The new version of KotlinX serialization usually drops almost immediately after the release of the new Kotlin version. I strongly recommend using it or pushing your team to start using it.

All hail KotlinX serialization!

--

--