要迭代一个 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
}
很奇怪getSize2
比getSize3
快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/