scala Iterator#foreach 性能问题

标签 scala scala-collections

要迭代一个 Iterator,我们可以调用它的 foreach 或使用 while 循环。 foreach的实现是:

def foreach[U](f: A =>  U) { while (hasNext) f(next()) }

所以我认为foreach应该和while(iterator.hasNext)一样快,但是经过一些测试后,结果让我非常惊讶。

我的测试代码:

def getSize2[T](i: Iterator[T]) = {
  var count = 0
  val f = (a: T) => count += 1
  while(i.hasNext) {
    f(i.next)
  }
  count
}

def getSize3[T](i: Iterator[T]) = {
  var count = 0
  val f = (a: T) => count += 1
  i.foreach(f)
  count
}

很奇怪getSize2getSize3快3倍!

有人知道那里发生了什么吗?

编辑: 粘贴我的测试程序

def main(args: Array[String]) {
  val data = 0 to 100000000

  val start2 = System.nanoTime
  (0 to 100).foreach(_ => getSize2(data.iterator))
  println("get size, while loop, using function: " + (System.nanoTime - start2)/1000000)

  val start3 = System.nanoTime
  (0 to 100).foreach(_ => getSize3(data.iterator))
  println("get size, foreach: " + (System.nanoTime - start3)/1000000)

}

我的操作系统:ubuntu 12.04,scala版本:2.10.3

最佳答案

while 循环更快,因为函数调用不是免费的,并且不能总是被 JIT 编译器删除。特别是,var count 被包裹在一个匿名对象中,因此它可以从函数对象中访问它,为了真正加快速度,JIT 编译器需要解开所有内容,然后最终意识到它根本不需要匿名对象。

将函数调用的额外层添加到库中的 foreach 确实会使 JIT 编译器的分析复杂化(间接层是三层而不是两层,等等)。

关于scala Iterator#foreach 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23693221/

相关文章:

scala - 使用代数验证和捕获错误

scala - 将函数的标记 HList(记录)映射到其参数类型的记录上

scala - 没有绑定(bind) play.api.db.slick.DatabaseConfigProvider 的实现

scala - 如何在 Scala 中实现惰性链模式

scala - 映射Scalaz验证失败

scala - 意外的Scala模式匹配语法

scala - 给定集合 A 和 B,是否有标准库函数来生成 3-ple(A - B、交集、B - A)?

scala - 从 scala.xml.NodeSeq 创建一个 Map

Scala 将集合转换为 map

scala 集合转字符串