Clojure性能优化与等效Java

时间:2015-01-02 00:17:21

标签: java performance clojure

加快此功能的最简单方法是什么?根据Criterium,java中的等效代码快了近50倍。

我敢打赌,如果我使用一个java数组并减少拳击量,这将有所帮助,但我想我先发布在这里,看看是否有任何基本的错误,我很容易修复。注意我已经为Clojure指出了(double ...),这大大提高了性能,但仍然没有像Java那样。我还首先使用(双数组...)而不是在函数内部使用(vec ...)来转换seq,这也提高了性能,但同样没有像Java那样。

(defn cosine-similarity [ma mb]
  (let [va (vec ma), vb (vec mb)]
    (loop [p (double 0)
           na (double 0)
           nb (double 0)
           i (dec (count va))]
      (if (neg? i)
        (/ p (* (Math/sqrt na) (Math/sqrt nb)))
        (let [a (double (va i))
              b (double (vb i))]
          (recur (+ p (* a b))
                 (+ na (* a a))
                 (+ nb (* b b))
                 (dec i)))))))

请注意,ma和mb都是seqs,每个包含200个双打。在java版本中,它们作为double [] args传递。

2 个答案:

答案 0 :(得分:5)

使用(double 0)没有直接指定0.0(双重文字)所带来的性能优势。

如果您将mamb作为double-array传递并将args提示为doubles,则不会将其转换为矢量{{}} {1}},并使用vec进行元素查找。这应该会给你留下非常接近java代码性能的东西。

如果使用双数组作为函数args,则不需要let块内的aget个调用。

最终结果应如下所示:

double

答案 1 :(得分:2)

您尝试添加吗?

(set! *unchecked-math* true)

由于你知道范围,你可以用它来获得额外的速度。

编辑:@noisesmith是对的,双数组输入输入会产生巨大的差异。

Edit2:在Alex Miller的评论之后获得快速的结果。

(set! *unchecked-math* true)

(defn ^double cosine-similarity
  [^doubles va ^doubles vb] 
    (loop [p 0.0
    na 0.0
    nb 0.0
    i  (dec (alength va))]
  (if (< i 0)
    (/ p (* (Math/sqrt na) (Math/sqrt nb)))
    (let [a  (aget va i)
          b  (aget vb i)]
       (recur (+ p (* a b))
         (+ na (* a a))
         (+ nb (* b b))
         (dec i))))))

 (defn rand-double-arr [n m]
   (double-array
     (take n (repeatedly #(rand m)))))

 (def ma (rand-double-arr 200 10000))
 (def mb (rand-double-arr 200 10000))

 ; using do times
 (dotimes [_ 30] (time (cosine-similarity ma mb)))
 ; ...
 ; "Elapsed time: 0.003537 msecs"

 ; using criterium: [criterium "0.4.3"]
 (use 'criterium.core)
 (quick-bench (cosine-similarity ma mb))
 ; 
 ; Execution time mean           : 2.072280 µs
 ; Execution time std-deviation  : 214.653997 ns
 ; Execution time lower quantile : 1.765412 µs ( 2.5%)
 ; Execution time upper quantile : 2.284536 µs (97.5%)
                   Overhead used : 6.128119 ns

第一个版本在500~1000毫秒范围内......