Skip to content

Pain point: JSON deserialization speed is slow in browser #907

Open
@SerVB

Description

@SerVB

JSON deserialization is extremely slow, at least on JS target. I've written the same deserializer myself like this:

  override fun decode(string: String): List<ServerEvent> {
    val jsonArray = JSON.parse<Array<Array<Any>>>(string)
    return jsonArray.map { it.toEvent() }
  }

  private fun Array<Any>.toEvent(): ServerEvent {
    val type = this[0] as String
    val content = this[1].unsafeCast<Json>()

    return when (type) {
      "a" -> ServerImageDataReplyEvent(
        content["a"].unsafeCast<Array<Any>>().toImageId(),
        content["b"].unsafeCast<Array<Any>>().toImageData()
      )
      "b" -> ServerPingReplyEvent(content["a"] as Int, content["b"] as Int)
      "c" -> ServerClipboardEvent(content["a"] as String)
// ...

It is tedious to write and support but it gives a boost of about 10x. Without the fix, deserialization takes about 80% of processing a message from server time. After the fix, it's only about 30% that is acceptable now.

To Reproduce
For convenience, I've extracted the protocol and deserializer part from our app and created a sample repo: https://github.com/SerVB/kx-serialization-js-benchmark.

I've taken a real JSON which is sent in our app with a size of 200 KB and tested the time of the following deserialization methods:

  • kotlinx.serialization.json.Json.parse
  • kotlinx.serialization.DynamicObjectParser.parse
  • 3 ways of manual deserialization: using JSON.parse and then reading it as JS objects.

It turns out that manual variants are about 20x faster. On smaller JSON sizes, the difference is smaller: in our app, the difference is 10x on average.

You can try it yourself: https://servb.github.io/kx-serialization-js-benchmark/index.html. Just click a button and it will print test results. You can find the results I've received in the README.

Possible solution
I think there should be an option to generate inlined code for deserializer. It's what I've done myself, but autogeneration is what I need here because I believe there can be many errors left in my manual code. Also, autogeneration will help to update deserialization immediately when we change our protocol.

Environment

  • Kotlin version: 1.3.72
  • Library version: 0.20.0
  • Kotlin platforms: JS
  • Gradle version: 6.1.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions