Are clojure multimethods slow in nature

I was looking at clojure.core re-groups:

(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))))) 

And I thought it would be “better” to rewrite it as a multimethod:

 (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))) 

However, comparing the time I was getting to see that rewriting is 4 times slower:

 clojure.core: "Elapsed time: 668.029589 msecs" multi-method: "Elapsed time: 2632.672379 msecs" 

Is this the natural result of multimethods, or is there something else wrong here?

+7
source share
3 answers

Clojure multimethods allow polymorphic runtime behavior based on arbitrary send functions. This is very powerful for creating hierarchies and ad hoc abstractions, but you pay for a performance hit for such flexibility. You might want to re-execute your decision using the protocol. Use only multimethods when you need complete flexibility at runtime.

+4
source

in general , anything that does more will take longer . because ellipses offer many ways to send, they will take longer than the protocols, which I have to answer your question "yes, compared to the protocols."

In practice, start with protocols and move on to multimethods when you need (and it looks like you need them). you cannot make a computer faster with code, but you can make it smaller

+3
source

I think the reason your implementation of several methods is slower may also be due to the fact that you use reduction in the lazy sequence (provided by the range) instead of the / recur loop on the increment index used in clojure.core.Try copying the loop part implement clojure.core / re-groups in the second defmethod and see if the performance improves.

It's hard to say without seeing your test case, but if there are many groups in a group, you can spend much more time reducing groups by vector than sending several methods. If there are several groups, then sending multiple methods will be a more significant part of the time spent. In any case, the same implementation eliminates the potential factor affecting the time difference and helps you better understand the actual overhead when sending multiple methods.

Another thing you can consider is the possibility that the slowdown is related to reflection. Try setting * warning-on-reflection * to true and see if it complains. Perhaps another piece of strategic advice might help.

0
source

All Articles