При тестировании типов коллекций Scala я наткнулся на аномалию.
Когда Array[Int]
объявлен в том же классе, что и Array[BoxedInt]
, производительность существенно меняется. На простых map
он работает быстрее, но в других тестовых случаях медленнее.
Я сравнил 2.11.8 и 2.12.8 (параметры по умолчанию), используя sbt-jmh (10 прогревов, 20 прогонов), и это происходит в обоих случаях. Когда штучный массив достаточно мал, эффект исчезает. Я попробовал это на List
, но не наблюдал того же эффекта.
Что происходит?
// 2.11.8 (without boxed array)
[info] Benchmark Mode Cnt Score Error Units
[info] ArrayBench.arrMap avgt 20 83.480 ± 0.525 us/op
[info] ArrayBench.arrMapFilter avgt 20 108.305 ± 1.329 us/op
[info] ArrayBench.arrMapFilterMap avgt 20 143.501 ± 1.688 us/op
[info] ArrayBench.arrMapMap avgt 20 176.429 ± 1.841 us/op
// 2.11.8 (with boxed array)
[info] Benchmark Mode Cnt Score Error Units
[info] ArrayBench.arrMap avgt 20 58.183 ± 0.747 us/op // faster
[info] ArrayBench.arrMapFilter avgt 20 150.008 ± 1.056 us/op // slower
[info] ArrayBench.arrMapFilterMap avgt 20 197.817 ± 2.061 us/op // slower
[info] ArrayBench.arrMapMap avgt 20 199.428 ± 2.548 us/op // slower
// 2.12.8 (without boxed array)
[info] Benchmark Mode Cnt Score Error Units
[info] ArrayBench.arrMap avgt 20 78.241 ± 0.520 us/op
[info] ArrayBench.arrMapFilter avgt 20 102.357 ± 1.294 us/op
[info] ArrayBench.arrMapFilterMap avgt 20 129.575 ± 1.035 us/op
[info] ArrayBench.arrMapMap avgt 20 167.233 ± 2.923 us/op
// 2.12.8 (with boxed array)
[info] Benchmark Mode Cnt Score Error Units
[info] ArrayBench.arrMap avgt 20 52.152 ± 0.506 us/op // faster
[info] ArrayBench.arrMapFilter avgt 20 148.799 ± 1.266 us/op // slower
[info] ArrayBench.arrMapFilterMap avgt 20 199.924 ± 1.883 us/op // slower
[info] ArrayBench.arrMapMap avgt 20 198.048 ± 13.489 us/op // slower
package benchmarks
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
class ArrayBench {
// case class BoxedInt(i: Int)
// var arr2: Array[BoxedInt] = _
var arr1: Array[Int] = _
@Setup
def setup: Unit = {
// arr2 = Array.range(10000, 1, -1).map(BoxedInt)
arr1 = Array.range(10000, 1, -1)
}
@Benchmark
def arrMap: Array[Int] = arr1.map(_ + 1)
@Benchmark
def arrMapMap: Array[Int] = arr1.map(_ + 1).map(_ * 2)
@Benchmark
def arrMapFilter: Array[Int] = arr1.map(_ + 1).filter(_ % 2 == 0)
@Benchmark
def arrMapFilterMap: Array[Int] = arr1.map(_ + 1).filter(_ % 2 == 0).map(_ * 2)
}