Groovy:for..in明显快于.each?

时间:2015-06-05 08:38:34

标签: groovy microbenchmark

我很好奇for..in出于性能原因应该优先考虑.each

2 个答案:

答案 0 :(得分:5)

For .. in是标准语言流控制的一部分。

相反,each会调用一个闭包,因此需要额外的开销。

.each {...}是与方法调用.each({...})

等效的语法糖

此外,由于它是一个闭包,在each代码块内部,您不能使用breakcontinue语句来控制循环。

http://kunaldabir.blogspot.it/2011/07/groovy-performance-iterating-with.html

更新了基准Java 1.8.0_45 Groovy 2.4.3:

  • 3327981每个{}
  • 320949 for(){

这是另一个100000次迭代的基准:

lines = (1..100000)
// with list.each {}
start = System.nanoTime()
lines.each { line->
    line++;
}
println System.nanoTime() - start

// with loop over list
start = System.nanoTime()
for (line in lines){
    line++;
}
println System.nanoTime() - start

结果:

  • 261062715 each {}
  • 64518703 for(){}

答案 1 :(得分:4)

让我们从动态完成调用以及使用Java逻辑更直接地完成调用(我将调用那些静态调用)的角度来理解事物。

for-in的情况下,Groovy在Iterator上运行,为了得到它,我们对iterator()进行了一次动态调用。如果我没有弄错,则使用普通的Java方法调用逻辑完成hasNext和next调用。因此,对于每次迭代,我们这里只有2个静态调用。根据基准测试,必须注意的是,第一次调用iterator()会导致严重的初始化时间,因为这可能会启动元类系统并且需要一些时间。

each的情况下,我们对每个自身进行动态调用,以及为open块创建对象(Closure的实例)。然后每个(Closure)也将调用iterator(),但是未缓存...以及所有一次性成本。在循环期间,hasNext和next使用Java逻辑完成,该逻辑进行2次静态调用。对Closure实例的调用是使用方法调用的java标准逻辑完成的,然后使用动态调用调用doCall。

总结一下,每次迭代for-in仅使用2次静态调用,而each则有3次静态调用和1次动态调用。动态调用比多个静态调用慢得多,并且对JVM进行优化要困难得多,因此主导了时序。因此,只要open块需要动态调用,each总是应该更慢。

由于Closure#调用的逻辑很复杂,因此很难优化动态调用。这很烦人,因为它并不是真正需要的,一旦找到解决方法就会被删除。如果我们能够取得成功,那么each可能仍然会变慢,但这是一件非常困难的事情,因为字节码大小和调用配置文件在这里起作用。理论上它们可以是相同的(忽略初始化时间),但JVM还有很多工作要做。当然,这同样适用于Java8中基于流的lambda处理的例子,