斯卡拉 - 不变性表演

时间:2012-11-16 10:46:34

标签: scala

注意:尽管我将使用Akka教程代码作为测试用例,但我的问题是关于Scala不变性性能,以及如何调整JVM

我正在接近Akka,我正在尝试其教程代码Akka getting started;我将源代码从教程粘贴到我自己的项目并运行它,获得此输出:

Calculation time:       660 milliseconds

在此之后,我尝试了一些关于代码的工作,重写了这个函数

def calculatePiFor(start: Int, nrOfElements: Int): Double = {
  var acc = 0.0
  for (i <- start until (start + nrOfElements))
    acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)
  acc
}

不使用 var 累加器,如此

def calculatePiFor(start: Int, nrOfElements: Int): Double = {
  val range = start until (start + nrOfElements)
  val computation = range.map(i => 4.0 * (1 - (i % 2) * 2) / (2 * i + 1))
  computation.sum
}
好吧,它有效,但表现有点退化

Calculation time:       1737 milliseconds

这里有我的问题:我的功能有什么重大错误或毫无疑问会导致这些表现吗?如果没有,有人能指出一个好的规则来调整JVM以改善这些性能吗?

我在Scala 2.9上运行我的代码,使用sbt 0.12.0及其默认的java选项(捆绑在sbt.bat中):

_JAVA_OPTS=-Xmx512M -XX:MaxPermSize=256m -XX:ReservedCodeCacheSize=128m

提前致谢

3 个答案:

答案 0 :(得分:7)

通过使用foldLeft来获得更好的表现,val computation = range.foldLeft(0.0){ case (sum,i) => sum + 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)) } 会在一次通过中计算总和作为您所示的必要示例:

{{1}}

但是,它可能仍然比它的命令对手慢。如果确实是您的应用程序的瓶颈,您应该使用最快的版本(带有while循环的命令式版本)。它对Actors是安全的,因为可变状态将是完全孤立的。

在所有其他情况下,最好使用凌乱的代码丢失几个百分比的性能(在整个应用程序中测量)。

答案 1 :(得分:2)

我认为你的程序中的问题是range.map(..)方法的调用,它是 strict (非懒惰)。这意味着 - 如果您的范围有1000个元素,map方法将为1000个新元素分配内存并立即计算它们。

所以我想改进代码的另一种非常简单的方法是使用范围视图:range.view.map(..),这样就可以懒惰地计算映射元素并分配恒定的内存量。

答案 2 :(得分:1)

您可以尝试使用foldLeft而不是map / sum:

  def calculatePiFor(start: Int, nrOfElements: Int): Double = {
    val range = start until (start + nrOfElements)
    range.foldLeft(0.0) { (acc,i) => acc + 4.0 * (1 - (i % 2) * 2) / (2 * i + 1)}
  } 

我猜这会避免需要中间集合,因此可能会运行得更快。