为什么这个Clojure程序这么慢?如何让它快速运行?

Iva*_*hin 5 java performance scala clojure

这里清楚地解释了如何优化处理原始值的Clojure程序:使用类型注释和未经检查的数学,它将快速运行:

(set! *unchecked-math* true)

(defn add-up ^long [^long n]
  (loop [n n i 0 sum 0]
    (if (< n i)
      sum
      (recur n (inc i) (+ i sum)))))
Run Code Online (Sandbox Code Playgroud)

所以,出于好奇,我已经尝试过了lein repl,令我惊讶的是,发现这段代码的运行速度比预期慢了20倍(Oracle JDK 1.8.0_11 x64上的Clojure 1.6.0):

user=> (time (add-up 1e8))
"Elapsed time: 2719.188432 msecs"
5000000050000000
Run Code Online (Sandbox Code Playgroud)

Scala 2.10.4(相同的JVM)中的等效代码在~90ms内运行:

def addup(n: Long) = { 
  @annotation.tailrec def sum(s: Long, i: Long): Long = 
    if (i == 0) s else sum(s + i, i - 1)
  sum(0, n)
}
Run Code Online (Sandbox Code Playgroud)

那么,我在Clojure代码示例中缺少什么?为什么它这么慢(理论上应该大致相同的速度)?

dno*_*len 18

基准测试lein repl通常是一个坏主意,因为它专门设置非服务器JVM设置.直接使用Clojure JAR,在OS X 10.9下运行JDK 8的3.5ghz i7 iMac上看到~40ms.

  • 我添加了`:jvm-opts ^:replace []`到我的`project.clj`,exec时间降到了80ms.谢谢!在这里找到了诀窍:http://stackoverflow.com/questions/20902479/clojure-performance-repl-versus-uberjar (3认同)

Thu*_*ail 5

@ dnolen的回答,一些观察结果:

虽然事实证明没有真正的区别,但我们应该让Clojure函数与Scala函数的形状相同.在

(defn add-up ^long [^long n]
  (loop [n n i 0 sum 0]
    (if (< n i)
      sum
      (recur n (inc i) (+ i sum)))))
Run Code Online (Sandbox Code Playgroud)
  • n没有被改变recur,所以不必束缚 loop.
  • 其余两个参数与Scala函数的顺序相反.
  • 它运行数字,而Scala运行.

修补这些不一致之处,我们得到了

defn add-up [^long n]
  (loop [sum 0, i n]
    (if (zero? i)
      sum
      (recur (+ sum i) (dec i)))))
Run Code Online (Sandbox Code Playgroud)

(Scala类型系统确保参数n转换为Longon on.据我了解(如果我错了请纠正我),Clojure ^long类型提示承诺Long很好地处理一个参数,但不承诺转换为Double类似1e8a Long.但是当我做出相应的修改时,我的结果非常不一致.)

在我的笔记本电脑上,上面给出了

(time (add-up 100000000))
"Elapsed time: 103.636782 msecs"
5000000050000000
Run Code Online (Sandbox Code Playgroud)

如果删除类型提示

(defn add-up [n]
  ...
  )
Run Code Online (Sandbox Code Playgroud)

...经过的时间乘以大约二十:

(time (add-up 100000000))
"Elapsed time: 2374.399915 msecs"
5000000050000000
Run Code Online (Sandbox Code Playgroud)
  • 这比删除未经检查的数学更有效,大约三倍于经过的时间.
  • 类型提示返回类型没有明显的效果.

所有这些都在OpenJDK Java 7上的Clojure 1.5.0上.