为什么JVM不优化简单回调(在Scala中)?



我正在创建 Scala 方法来将元素添加到ArrayBuffer中。我正在考虑两种方法:

  • def addToArrayBuffer(b: ArrayBuffer[Int])
  • def addToArrayBuffer(cb: Int => Unit)

第一种方法是获取集合并将元素添加到其中的方法。第二种方法是获取回调cb并为我要添加到集合中的每个元素调用此回调的方法。

第二种方法更灵活,因为我可以在将元素添加到集合之前转换/过滤元素。

不幸的是,第二种方法较慢(72 ops/s vs 57 ops/s(:

Benchmark                                        Mode  Cnt   Score    Error  Units
TestBenchmark.addToArrayBufferDirectly          thrpt    9  72.808 ? 13.394  ops/s
TestBenchmark.addToArrayBufferViaCallback       thrpt    9  57.786 ?  3.532  ops/s

我的问题是为什么 JVM 无法优化回调并达到与直接添加到集合中相同的速度?如何提高速度?

我在 Mac 上使用java版本1.8.0_162。以下是基准测试的来源:

package bench
import org.openjdk.jmh.annotations.{Benchmark, Fork, Measurement, Scope, State, Warmup}
import org.openjdk.jmh.infra.Blackhole
import scala.collection.mutable.ArrayBuffer
@State(Scope.Thread)
@Warmup(iterations = 5)
@Measurement(iterations = 3)
@Fork(3)
class TestBenchmark {
  val size = 1000000
  @Benchmark
  def addToArrayBufferDirectly(blackhole: Blackhole) = {
    def addToArrayBuffer(b: ArrayBuffer[Int]) = {
      var i = 0
      while (i < size) {
        b.append(i)
        i += 1
      }
    }
    val ab = new ArrayBuffer[Int](size)
    addToArrayBuffer(ab)
    blackhole.consume(ab)
  }
  @Benchmark
  def addToArrayBufferViaCallback(blackhole: Blackhole) = {
    def addToArrayBuffer(cb: Int => Unit) = {
      var i = 0
      while (i < size) {
        cb(i)
        i += 1
      }
    }
    val ab = new ArrayBuffer[Int](size)
    addToArrayBuffer(i => ab.append(i))
    blackhole.consume(ab)
  }
}

它可以由 Scala 编译器通过使用标志进行优化

scalacOptions ++= Seq(
  "-opt-inline-from:bench.**",
  "-opt:l:inline"
)

无需更改代码。更多关于 Scala 内联: https://www.lightbend.com/blog/scala-inliner-optimizer

最新更新