[英]Are clojure multimethods slow by nature
我在看 clojure.core函數重組:
(defn re-groups [^java.util.regex.Matcher m]
(let [gc (. m (groupCount))]
(if (zero? gc)
(. m (group))
(loop [ret [] c 0]
(if (<= c gc)
(recur (conj ret (. m (group c))) (inc c))
ret)))))
並認為將其重寫為多方法會“更好”:
(defmulti re-groups (fn [^java.util.regex.Matcher m] (.groupCount m)))
(defmethod re-groups 0 [m] (.group m))
(defmethod re-groups :default [m]
(let [idxs (range (inc (.groupCount m)))]
(reduce #(conj %1 (.group m %2)) [] idxs)))
然而,當比較我被驚訝的時候看到重寫慢4倍:
clojure.core: "Elapsed time: 668.029589 msecs"
multi-method: "Elapsed time: 2632.672379 msecs"
這是多方法的自然結果還是存在其他問題?
Clojure multimethods允許基於任意調度函數的運行時多態行為。 這對於構建臨時層次結構和抽象非常強大,但是您為這種靈活性付出了性能損失。 您可能希望使用協議重新實現您的解決方案。 只有在需要完整的運行時類型靈活性時才使用多方法。
一般而言, 任何做得更多的事都需要更多時間 。 因為多方法提供了多種方式來發送,所以它們需要的時間比我必須回答的問題要長“是的,與協議相比”。
在實踐中,從協議開始,並在必要時轉到多方法(看起來你需要它們)。 你不能用代碼使計算機更快,但你可以減少它
我認為你的多方法實現較慢的原因也可能是因為你在一個惰性序列(由范圍提供)上使用reduce而不是在clojure.core中使用的遞增索引上使用loop / recur。 嘗試將clojure.core / re-groups實現的循環部分復制到第二個defmethod中,看看是否不會提高性能。
很難說沒有看到你的測試用例,但如果匹配器中有很多組,你可能會花費更多的時間將組減少為向量而不是多方法調度。 如果雖然有很少的組,那么多方法調度將花費更多的時間。 在任何一種情況下,具有相同的實現將消除對時序差異的潛在影響因素,並幫助您更好地感受多方法分派中的實際開銷。
您可能考慮的另一件事是減速可能是由於反射造成的。 嘗試將* warn-on-reflection *設置為true,看看是否有抱怨。 也許另一種戰略類型提示可能有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.