Performance 为什么对这个Scala代码进行一个小小的更改会对性能产生如此巨大的影响?

Performance 为什么对这个Scala代码进行一个小小的更改会对性能产生如此巨大的影响?,performance,optimization,scala,Performance,Optimization,Scala,我在32位(挤压)系统(2.5GHz CPU)上运行,sun-java6 6.24-1,但带有来自Wheezy的Scala 2.8.1软件包 此代码使用scalac-optimize编译,运行时间超过30秒: object Performance { import scala.annotation.tailrec @tailrec def gcd(x:Int,y:Int):Int = { if (x == 0) y else gcd(y%x

我在32位(挤压)系统(2.5GHz CPU)上运行,sun-java6 6.24-1,但带有来自Wheezy的Scala 2.8.1软件包

此代码使用
scalac-optimize
编译,运行时间超过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)
  }
}

但如果我在代码< >代码>主< <代码>范围内进行“<代码> ValsEs= >代码的微小改变,那么它只运行1秒,这更像是我期待看到的,并且与等效C++的性能相媲美。有趣的是,将

vales=
保留在原来的位置,但使用
lazy
对其进行限定也具有相同的加速效果


这是怎么回事?为什么在函数范围外执行计算会慢得多?

JVM没有将静态初始值设定项优化到与优化方法调用相同的级别。不幸的是,当你在那里做很多工作时,这会影响性能——这是一个完美的例子。这也是为什么老的
应用程序
特性被认为有问题的原因之一,也是为什么Scala 2.9中有一个
DelayedInit
特性,它获得了一些编译器帮助,可以将内容从初始值设定项移动到稍后调用的方法中



(编辑:将“构造函数”固定为“初始值设定项”。相当长的打字错误!)

顶级对象块中的代码被转换为对象类上的静态初始值设定项。Java中的等价物是

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

HotSpot JVM不会对静态初始化过程中遇到的代码执行任何优化,根据合理的启发,此类代码只运行一次

我在2.8.1(Sun Java 1.6.0_24-b07)中看到了同样的事情,没有
-optimize
,FWIW。提到optimize有点像是在转移视线;我从未见过使用它(或不使用它)对我所研究的任何Scala东西的性能有任何显著的影响。有趣的是,同样的事情也发生在使用Scala 2.9.0的类似系统上,无论是否使用-optimize。是的,这很吸引人。我想到这一点的时候,有人上一次接触Java是在90年代中期,但Scala是一种比Java更有吸引力的语言(无论如何,对我来说),这一点吸引了我回到JVM。因此,当底层技术的现实被这样的事情意外地揭示出来时,我确实会感到非常惊讶。特别是在不变性的情况下,我怀疑在类的初始化阶段进行大量计算是一种常见的模式。@ziggystar-这特别影响静态初始值设定项。而且由于它只影响静态初始值设定项,所以它只影响Scala单例对象,而不影响类对象。这是Scala特有的还是一般正确的?因为我不明白为什么JIT不能优化普通java代码中的长静态初始值设定项(或者更确切地说,为什么它能区分静态代码块和普通代码)@Voo,这在一般情况下是正确的,甚至会影响java静态代码。它似乎是一个JVM设计;一个可能的假设是:假设静态初始值设定项只运行一次,JVM的作者不想花那么多时间优化他们的用例。@timday-将惰性添加到每一个昂贵的计算中会很累。有一个能同时完成这一切的机制真是太好了。@notnoop我不明白为什么。确保静态初始值设定项没有优化——就像任何只调用一次的主方法都不会优化一样。但是,如果我们内部有一些复杂的计算,为什么不按照通常的规则进行JIT呢?@notnoop。另一方面,静态初始值设定项实际上只能运行一次(每个类加载器),并且具有非平凡的语义:检查JLS 12.4.2。JVM设计者基本上认为这不值得付出努力。另一方面,main方法需要进行大量优化,以实际提高代码的速度。另外,
main
方法就像任何其他静态方法一样,实际上可以运行多次(实际上您可以自己调用main方法)。