I use the json4s library in many projects, and I recently noticed a weird behavior that I'm not able to explain while debugging some unit tests.
The issue is quite simple: when serializing a case class into a string, the first serialization performed takes an extremely long time.
Subsequent write
operations, go much, much faster.
Here is some sample code that displays this behavior :
import org.json4s.{DefaultFormats, Formats}
import org.json4s.jackson.Serialization.write
object Timer {
def time[R](message : String)(block: => R): R = {
val t0 = System.nanoTime()
val result = block
val t1 = System.nanoTime()
println(s"$message : Elapsed time: " + (t1 - t0)*1e-6 + "ms")
result
}
}
case class Dummy(attr1 : String, attr2 : Int, attr3 : String, attr4 : Long, attr5 : Option[Int])
object Main extends App {
implicit val formats: Formats = DefaultFormats
val obj = Dummy("hello", 3, "", 4L, None)
Timer.time("First pass") {
write(obj)
}
Timer.time("Second pass") {
write(obj)
}
}
On my computer, this displays:
First pass : Elapsed time: 503.460855ms
Second pass : Elapsed time: 0.569251ms
I am using:
- scala version 2.13.3
- json4s-jackson version 3.6.10
If you want to reproduce this issue, I have created a small repository with this example : https://github.com/Thundzz/json4s-perf-check
I could not find any resource mentioning this performance issue, be it on the json4s readme, in the github issues or here on stackoverflow.
To be clear, this is not very problematic since only the first execution takes a lot of time, but I expected this behavior to maybe be documented somewhere at least, which doesn't seem to be the case.
- Am I doing something wrong in my usage of the library ?
- Is this expected behavior ? I am also wondering if this would warrant an issue or PR on the json4s repository. Even just mentioning this performance quirk in the readme would probably be helpful to other users who might get surprised when they notice this too.
Notes:
- I know that the code example above is not a proper benchmark. However, it is quite hard to benchmark a "first call" to a function by using tools like ScalaMeter, and although it has many flaws, the code above produces consistent timing behaviour (the first call is orders of magnitude slower in terms of execution time)