为什么对这个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) } }
但是,如果我把这个小小的变化移动到main
的范围内,那么它会在1秒内运行,这更像是我希望看到的,与相当于C ++的性能相当。 有趣的是,离开这个地方,但是用lazy
排位它也有同样的加速效果。
这里发生了什么? 为什么在function范围之外执行这个计算要慢得多?
JVM不会优化静态初始化器(就是这样)到与优化方法调用相同的级别。 不幸的是,当你在那里做了很多工作,那会伤害到performance – 这就是一个很好的例子。 这也是为什么旧的Application
特性被认为是有问题的原因之一,为什么在Scala 2.9中有一个DelayedInit
特性,它获得了一些编译器帮助将东西从初始化器移动到稍后调用的方法中。
(编辑:固定“构造”到“初始化”,而不是冗长的错字!)
顶级对象块中的代码被转换为对象类的静态初始化器。 Java中的等价物将是
class Performance{ static{ //expensive calculation } public static void main(String[] args){ //use result of expensive calculation } }
HotSpot JVM不会对静态初始化程序期间遇到的代码执行任何优化,在这样的代码只运行一次的合理启发下。