尽管是包装器,但 CollectionConverters 的性能却很昂贵?

Costly performance of CollectionConverters despite being wrappers?

AFAIU,CollectionConverters are wrappers simply delegating calls to the underlying collection, thus the cost should be single object allocation and then single method call indirection, for example, Conversion Between Java and Scala Collections 状态

Internally, these conversion work by setting up a “wrapper” object that forwards all operations to the underlying collection object. So collections are never copied when converting between Java and Scala.

分析 SetWrapper 我们确实看到它只是将调用委托给 underlying 集合

class SetWrapper[A](underlying: Set[A]) extends ju.AbstractSet[A] with Serializable { self =>
  ...
  def size = underlying.size
  ...
}

但是,请考虑以下 jmh 基准测试

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

如果 CollectionConverters 是简单的包装器,为什么似乎会有明显的性能损失?

问题的灵感来源于

在2.12中,scalaSet.sizejust a field access; for 2.13 it's a trivial field accessor call on a final class,应该很容易内联,所以

a single object allocation and then single method call indirection

与相比,可能是一个不小的开销。

我不明白的是 javajavaAsScala 之间的区别(编辑:呃,这是一种间接的方式)。