【问题标题】:How does Scala's .min avoid the penalty of boxing and unboxing?Scala 的 .min 如何避免装箱和拆箱的惩罚?
【发布时间】:2012-05-24 06:48:04
【问题描述】:

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

我看到使用 min 的时间约为 20 毫秒,使用 reduceLeft 的时间约为 200 毫秒。我已经使用YourKit 分析了这段代码;这是调用树的screen grab,显示min 不会导致任何装箱。

【问题讨论】:

  • 抱歉,我现在没有时间检查自己,但显然,您在自己的实现中使用reduce 而不是reduceLeft。是否可以解释放缓?
  • “没有装箱或拆箱” - 你是怎么得出这个结论的(我不相信)
  • @paradigmatic,哎呀,已修复,不会影响我看到的内容。
  • 如果您改用val cmp = implicitly[Ordering[java.util.Long]] 会怎样?这会影响时间吗?
  • @oxbow_lakes,我已经包含了我的分析代码,以及来自 YourKit 的屏幕截图,显示 reduceLeft 拳击,min 没有。

标签: performance scala boxing


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多