I'd like to know the cost of implicit conversion from java collection to scala ones. In this doc there are several implicit two-way conversions for which it is said that "converting from a source type to a target type and back again will return the original source object".
I conclude that the cost should be minor (wrapping), but still how much is it?
I ask this question because I use java sets in some scala code, which is implicitly converted to scala set as I import asScalaSet
(I do need it in some places). However, it might be a consequent overhead for very little accessors such as size()
Does anyone know?
I decided to answer your question from practical point of view. I used the following simple JMH benchmarks to test operations per second for original scala collection and converted one (using implicit conversion).
Please find below code of benchmark:
import org.openjdk.jmh.annotations._
import scala.collection.JavaConversions._
@State(Scope.Thread)
class WrapperBenchmark {
val unwrappedCollection = (1 to 100).toSet
val wrappedCollection: java.util.Set[Int] = (1 to 100).toSet[Int]
@Benchmark
def measureUnwrapped: Int = unwrappedCollection.size
@Benchmark
def measureWrapped: Int = wrappedCollection.size()
}
I used sbt and sbt-jmh plugin for running. Please find results below:
[info] Benchmark Mode Cnt Score Error Units
[info] WrapperBenchmark.measureUnwrapped thrpt 200 353214968.844 ± 1534779.932 ops/s
[info] WrapperBenchmark.measureWrapped thrpt 200 284669396.241 ± 4223983.126 ops/s
So basically according to results, there is overhead indeed. I will try to continue my research providing the reason why it is like this in later update to this question.
Please let me know if you want me to share complete sbt project for your future research.
Here are jmh benchmark results using Scala 2.13.1 CollectionConverters
import org.openjdk.jmh.annotations._
import scala.jdk.CollectionConverters._
import java.{util => ju}
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class So31830028 {
val size = 1000000
val scalaSet: Set[Int] = (1 to size).toSet
val javaSet: ju.Set[Int] = (1 to size).toSet.asJava
@Benchmark def scala = scalaSet.size
@Benchmark def scalaAsJava = scalaSet.asJava.size
@Benchmark def java = javaSet.size
@Benchmark def javaAsScala = javaSet.asScala.size
}
where sbt "jmh:run -i 10 -wi 5 -f 2 -t 1 bench.So31830028"
gives
[info] Benchmark Mode Cnt Score Error Units
[info] So31830028.java thrpt 20 356515729.840 ± 64691657.672 ops/s
[info] So31830028.javaAsScala thrpt 20 270053471.338 ± 36854051.611 ops/s
[info] So31830028.scala thrpt 20 448415156.726 ± 53674976.259 ops/s
[info] So31830028.scalaAsJava thrpt 20 211808793.234 ± 57898858.737 ops/s
Indeed there seems to be a rather significant cost.