Scala的.min如何避免拳击和拆箱的惩罚?

时间:2012-05-24 06:48:04

标签: performance scala boxing

Vector.minimplemented

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中的时间,否则它会占据主导地位,是的,我将基准测试加热了......)。

  

第一个版本如何避免拳击的性能损失?

编辑:这是我的分析代码:

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))
}

我看到时间使用大约20毫秒的min,使用大约200毫秒的reduceLeft。我使用YourKit描述了此代码;这是调用树的screen grab,显示min不会导致任何拳击。

1 个答案:

答案 0 :(得分:5)

我认为第一个版本会为java.lang.Long推断B。所以仍然有拳击正在进行,但只有在填充向量时,之后所有的比较都在盒装对象之间。

在第二个版本中,由于cmp的类型为Ordering[Long],因此向量中的java.lang.Long必须在传递给cmp.lteq(x, y)之前取消装箱。