为什么函数调用会减少clojure中的内容?

时间:2013-11-10 04:28:22

标签: performance clojure

我一直在玩Is Clojure is Still Fast?(和前传Clojure is Fast)代码。似乎不幸的是,将微分方程(f)内联是改善性能所采取的步骤之一。我没有这样做的最干净/最快的事情如下:

; As in the referenced posts, for giving a rough measure of cycles/iteration (I know this is a very rough
; estimate...)
(def cpuspeed 3.6) ;; My computer runs at 3.6 GHz
(defmacro cyclesperit [expr its]
  `(let [start# (. System (nanoTime))
         ret# ( ~@expr (/ 1.0 ~its) ~its )
         finish# (. System (nanoTime))]
     (println (int (/ (* cpuspeed (- finish# start#)) ~its)))))

;; My solution
(defn f [^double t ^double y] (- t y))
(defn mysolveit [^double t0 ^double y0 ^double h ^long its]
  (if (> its 0)
    (let [t1 (+ t0 h)
          y1 (+ y0 (* h (f t0 y0)))]
      (recur t1 y1 h (dec its)))
    [t0 y0 h its]))
; => 50-55 cycles/it

; The fastest solution presented by the author (John Aspden) is
(defn faster-solveit [^double t0 ^double y0 ^double h ^long its]
  (if (> its 0)
    (let [t1 (+ t0 h)
          y1 (+ y0 (* h (- t0 y0)))]
      (recur t1 y1 h (dec its)))
    [t0 y0 h its]))
; => 25-30 cycles/it

我的解决方案中的类型提示有很大帮助(它是224个周期/它在fsolveit上没有类型提示),但它仍然比内联版本慢近2倍。最终这种表现仍然相当不错,但这种打击很不幸。

为什么会出现这样的性能影响?有办法解决吗?有没有计划找到改善这种方法的方法?正如John在原帖中指出的那样,函数调用在函数式语言中效率低下似乎很有趣/不幸。

注意:我正在运行Clojure 1.5并且在project.clj文件中有:jvm-opts ^:replace [],这样我就可以使用lein exec / run而不会减慢速度(如果你不这样做,我会这样做)发现...)

1 个答案:

答案 0 :(得分:10)

在有JIT编译器的情况下进行基准测试很棘手;你真的必须允许一个预热期,但是你也不能只是循环运行它,因为它可能被证明是无操作并且优化了。在Clojure中,通常的解决方案是使用Hugo Duncan的Criterium

(solveit 0.0 1.0 (/ 1.0 1000000) 1000000)的两个版本运行solveit的Criterium基准测试,导致我的计算机上的时间几乎完全相同(mysolveit ~3.44 ms,faster-solveit ~3.45女士)。这是使用Criterium 0.4.2(-XX:+UseConcMarkSweepGC)运行criterium.core/bench的64位JVM。据推测,HotSpot只是内联f。在任何情况下,根本没有性能损失。