Clojure性能优化与等效Java

Sco*_*ach 2 java performance clojure

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

我敢打赌,如果我使用Java Array并减少装箱数量,这将大有帮助,但我想我会先在这里发布,看看是否有我犯的任何基本错误,可以很容易地解决。注意,我已经为Clojure指出了(double ...),它极大地提高了性能,但仍然没有Java那样。我还首先使用(double-array ...)而不是在函数内部使用(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)))))))
Run Code Online (Sandbox Code Playgroud)

请注意,ma和mb都是序列,每个序列包含200个Double。在Java版本中,它们作为double [] args传递。

noi*_*ith 5

使用(double 0)没有性能上的好处,0.0直接指定(双字面量)不会带来任何好处。

如果您传入mambas double-array并将args暗示为doubles,不要将它们转换为vector vec,而是使用aget进行元素查找,则会获得明显更好的性能。这应该使您获得非常接近Java代码性能的东西。

double如果使用双数组作为函数参数的让利块内部通话将不再需要。

最终结果应如下所示:

(defn cosine-similarity [^doubles ma ^doubles mb]
  (loop [p 0.0
         na 0.0
         nb 0.0
         i (dec (count va))]
    (if (neg? i)
      (/ 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))))))
Run Code Online (Sandbox Code Playgroud)