簡體   English   中英

為什么這個Clojure代碼與Java中的替代代碼相比如此之慢?

[英]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中的新功能(目前處於測試階段,還沒有完全出現),但在這里特別有用。

我改變了一些重要的事情:

  • 我替換了areduce - areduce的問題是它不知道你的數組的原始類型。 通過編寫自己的內部循環,您可以利用提示。 有可能提示areduce的主體以使其工作但我傾向於在進行原始數學時更喜歡顯式循環。
  • 我在需要的地方使用^長提示,因為Clojure只支持原始的長參數/返回,而不是int。 將根據需要插入正確的基本轉換。 如果需要,有些函數可以獲得原始的int溢出語義。
  • rem可以轉到mod不能的原始操作。 我認為這里的語義與你正在做的事情是一樣的。 這是大多數拳擊的來源。
  • 我正在使用int-array而不是其他方式來制作數組。 我認為這是你正在做的最好的方式。

您可以將兩個循環合並為一個,並進一步提高性能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM