[英]Why is this Clojure code so slow compared to the alternative in Java?
tl; dr:為什么代碼如此慢?
我嘗試優化以下代碼以提高速度; 它的目的是在進行n ^ 2次操作時將一個數組(大小n = 1000)轉換為另一個(大小相同),轉換的細節現在並不重要。
因為我試圖盡可能快地獲得速度,所以我盡可能使用Java原語; 仍然,我得到的通常是每個'轉換'調用大約70毫秒。 重寫到Java時,平均調用時間小於2毫秒。
1)哇,Java很快
2)哇,Clojure很慢
3)你能解釋一下,為什么會這樣? 天真地,我希望Clojure能夠生成一個代碼字節碼,它應該非常接近Java,為什么不是這樣呢?
4)我不是百分百肯定如何使用這些^提示,也許我弄錯了?
(defn transform [^ints src]
(let [res ^ints (make-array Integer/TYPE 1000)]
(loop [x (int 0)]
(if (= 1000 x) res
(do
(aset res x (areduce src i ret (int 0)
(+ ret (* (mod x 2) (mod i 3) (aget src i)))))
(recur (inc x)))))))
(let [arr (into-array Integer/TYPE (range 1000))]
(doseq [_ (range 20)]
(println (time (transform arr)))
))
這樣的事情應該更接近:
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(defn inner ^long [^ints src ^long x]
(let [len (alength src)]
(loop [i 0 acc 0]
(if (< i len)
(recur (inc i) (+ acc (* (rem x 2) (rem i 3) (aget src i))))
acc))))
(defn transform [^ints src]
(let [res ^ints (int-array 1000)]
(loop [x 0]
(if (= 1000 x)
res
(do
(aset res x (inner src x))
(recur (inc x)))))))
(defn bench []
(let [arr (int-array (range 1000))]
(doseq [_ (range 20)]
(println (time (transform arr))))))
頂部設置對於檢測錯誤很有用。 :warn-on-boxed one是Clojure 1.7中的新功能(目前處於測試階段,還沒有完全出現),但在這里特別有用。
我改變了一些重要的事情:
您可以將兩個循環合並為一個,並進一步提高性能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.