Почему JVM не оптимизирует простой обратный вызов (в Scala)? - PullRequest
0 голосов
/ 12 июня 2019

Я создаю метод Scala для добавления элементов в ArrayBuffer.Я думаю о 2 подходах:

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

Первый подход - это метод, который получает коллекцию и добавляет элементывнутрь.Второй подход - это метод, который получает обратный вызов cb и вызывает этот обратный вызов для каждого элемента, который я хочу добавить в коллекцию.

Второй подход является более гибким, поскольку я могу преобразовывать / фильтровать элементы перед добавлением их в коллекцию.

К сожалению, второй подход медленнее (72 операции / с против 57 операций / с):

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 не может оптимизировать обратный вызов и достичь той же скорости, что ипрямое добавление в коллекцию?И как мне улучшить скорость?

Я использую java версию 1.8.0_162 на Mac.Вот источник бенчмарка:

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 Ответ

1 голос
/ 14 июня 2019

Это может быть оптимизировано компилятором Scala с использованием флагов

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

Никаких изменений в коде не требуется. Подробнее о вкладке Scala: https://www.lightbend.com/blog/scala-inliner-optimizer

...