Yet another post about performance and microbenchmarks. Yes, I know.

Very small JMH benchmark:

@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 2, jvmArgs = Array("-Xmx2G"))
@Measurement(iterations = 7, time = 3, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS)
class StringBuilderBenchmark {

  @Benchmark
  def javaStringBuilder: String = {
    new java.lang.StringBuilder()
      .append("abc").append("def")
      .toString
  }

  @Benchmark
  def javaStringBuilder2: String = {
    new java.lang.StringBuilder()
      .append(495-char-length-string).append(495-char-length-string)
      .toString
  }

  @Benchmark
  def scalaStringBuilder: String = {
    new scala.collection.mutable.StringBuilder()
      .append("abc").append("def")
      .toString
  }

  @Benchmark
  def scalaStringBuilder: String = {
    new scala.collection.mutable.StringBuilder()
      .append(495-char-length-string).append(495-char-length-string)
      .toString
  }
}

And the result:

Benchmark            Mode  Cnt    Score   Error  Units
javaStringBuilder    avgt   14    8.754 ± 0.465  ns/op
javaStringBuilder2   avgt   14  237.280 ± 0.828  ns/op
scalaStringBuilder   avgt   14   27.299 ± 0.096  ns/op
scalaStringBuilder2  avgt   14  720.742 ± 3.528  ns/op

Wow. Apparently, JVM doesn’t do some optimization over wrapped StringBuilder. I’ve noticed this performance degradation on a more complex test, so, I don’t think it’s just about “microbenchmarking is evil”.

The saddest part, that Scala developers “suffer” just to have StringBuilder as a collection. But do we really need collection features from it? I doubt it.

Source code is on GitHub.

Originally posted on Medium.