为什么对此Scala代码进行少量更改会对性能产生如此巨大的影响?

时间:2011-06-07 13:25:00

标签: performance optimization scala

我正在使用32位Debian 6.0(Squeeze)系统(2.5 GHz Core 2 CPU),sun-java6 6.24-1,但使用Wheezy的Scala 2.8.1软件包。

此代码使用scalac -optimise编译,运行时间超过30秒:

object Performance {

  import scala.annotation.tailrec

  @tailrec def gcd(x:Int,y:Int):Int = {
    if (x == 0)
      y 
    else 
      gcd(y%x,x)
  }

  val p = 1009
  val q = 3643
  val t = (p-1)*(q-1)

  val es = (2 until t).filter(gcd(_,t) == 1)
  def main(args:Array[String]) {
    println(es.length)
  }
}

但是,如果我将val es=一行向下移动并在main范围内进行微不足道的改变,那么它只需1秒就可以运行,这更像是我期待看到的并且与等效C ++的性能相当。有趣的是,将val es=保留在lazy但使其符合{{1}}也具有相同的加速效果。

这里发生了什么?为什么在函数范围之外执行计算要慢得多?

2 个答案:

答案 0 :(得分:51)

JVM不会将静态初始化程序(这就是它)优化到优化方法调用的同一级别。不幸的是,当你在那里做很多工作时,这会伤害性能 - 这就是一个很好的例子。这也是旧Application特性被认为有问题的一个原因,以及为什么Scala 2.9中存在DelayedInit特征,它有一些编译器帮助将东西从初始化器转移到一个被调用的方法中以后。


(编辑:修复“构造函数”到“初始化程序”。相当冗长的拼写错误!)

答案 1 :(得分:40)

顶级对象块中的代码被转换为对象类的静态初始化程序。 Java中的等价物是

class Performance{
    static{
      //expensive calculation
    }
    public static void main(String[] args){
      //use result of expensive calculation
    }
}

HotSpot JVM不会对静态初始化期间遇到的代码执行任何优化,在合理的启发式规则下,此类代码只运行一次。