Vector.min
是 implemented作为
def min[B >: A](implicit cmp: Ordering[B]): A = {
if (isEmpty)
throw new UnsupportedOperationException("empty.min")
reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
}
当你配置文件时
Vector.fill(1000000)(scala.util.Random.nextLong).min
它很快,而且没有装箱或拆箱。但是,如果您编写了明显等效的
val cmp = implicitly[Ordering[Long]]
Vector.fill(1000000)(scala.util.Random.nextLong).reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
它运行速度大约慢 10 倍(忽略 Random 中的时间,否则它会主导这一点,是的,我使我的基准测试升温......)。
How is the first version avoiding the performance penalty of the boxing?
编辑:这是我的分析代码:
val cmp = implicitly[Ordering[Long]]
def randomLongs = Vector.fill(1000000)(scala.util.Random.nextLong)
def timing[R](f: => R): (Long, R) = {
val startTime = System.nanoTime
val result = f
((System.nanoTime - startTime) / 1000000, result)
}
def minTiming = { val r = randomLongs; timing(r.min)._1 }
def reduceLeftTiming = { val r = randomLongs; timing(r.reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y))._1 }
while(true) {
println((minTiming, reduceLeftTiming))
}
我看到使用
min
的次数大约 20 毫秒,使用 reduceLeft
约 200 毫秒。我已经使用 YourKit
分析了此代码;这是一个 screen grab的调用树显示 min
不会导致任何拳击。
最佳答案
我认为第一个版本推断 java.lang.Long
为 B
.所以仍然有装箱,但只是在填充向量时,然后所有比较都在装箱对象之间进行。
在第二个版本中,由于 cmp
的类型给出为 Ordering[Long]
, java.lang.Long
向量中的 s 在传递给 cmp.lteq(x, y)
之前必须先拆箱.
关于performance - Scala 的 .min 如何避免装箱和拆箱的惩罚?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10732496/