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

时间:2019-06-12 08:23:52

标签: scala optimization jvm benchmarking

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

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

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

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

不幸的是,第二种方法比较慢(72 ops / s与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上使用java1.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)
  }
}

1 个答案:

答案 0 :(得分:1)

Scala编译器可以使用标志对其进行优化

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

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