从 java 到 scala 集合的隐式转换成本
Cost of implicit conversion from java to scala collections
我想知道从 java 集合到 scala 集合的隐式转换的成本。在 this doc 中有几个隐式双向转换,据说 "converting from a source type to a target type and back again will return the original source object".
我的结论是成本应该很小(包装),但还是多少钱?
我问这个问题是因为我在某些 Scala 代码中使用了 java 集,当我导入 asScalaSet
时它被隐式转换为 Scala 集(我在某些地方确实需要它)。但是,对于 size()
等非常小的访问器来说,这可能是一个随之而来的开销
有人知道吗?
我决定从实际的角度来回答你的问题。我使用以下简单的 JMH 基准来测试原始 scala 集合的每秒操作数并转换一个(使用隐式转换)。
请找到以下基准代码:
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()
}
我为 运行 使用了 sbt 和 sbt-jmh 插件。请在下面查找结果:
[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
所以基本上根据结果,确实有开销。我将尝试继续我的研究,在稍后更新此问题时提供原因。
如果你想让我分享完整的 sbt 项目以供你未来研究,请告诉我。
这是使用 Scala 2.13.1 的 jmh 基准测试结果 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
}
其中 sbt "jmh:run -i 10 -wi 5 -f 2 -t 1 bench.So31830028"
给出
[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
确实有一笔相当大的费用。
我想知道从 java 集合到 scala 集合的隐式转换的成本。在 this doc 中有几个隐式双向转换,据说 "converting from a source type to a target type and back again will return the original source object".
我的结论是成本应该很小(包装),但还是多少钱?
我问这个问题是因为我在某些 Scala 代码中使用了 java 集,当我导入 asScalaSet
时它被隐式转换为 Scala 集(我在某些地方确实需要它)。但是,对于 size()
有人知道吗?
我决定从实际的角度来回答你的问题。我使用以下简单的 JMH 基准来测试原始 scala 集合的每秒操作数并转换一个(使用隐式转换)。
请找到以下基准代码:
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()
}
我为 运行 使用了 sbt 和 sbt-jmh 插件。请在下面查找结果:
[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
所以基本上根据结果,确实有开销。我将尝试继续我的研究,在稍后更新此问题时提供原因。
如果你想让我分享完整的 sbt 项目以供你未来研究,请告诉我。
这是使用 Scala 2.13.1 的 jmh 基准测试结果 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
}
其中 sbt "jmh:run -i 10 -wi 5 -f 2 -t 1 bench.So31830028"
给出
[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
确实有一笔相当大的费用。