问题
Multimethods are slower than protocols and one should try to use protocols when they can solve the problem, even though using multimethods gives a more flexible solution.
So what is the case with cond
and multimethod? They can be used to solve the same problem but my guess is that multimethod has a huge performance overhead vs cond
. If so why would i ever want to use multimethod instead of cond
?
回答1:
To follow up on @AlexMiller comment, I tried to benchmark with more randomised data and added up the protocol implementation (also added one more type - Integer - to the different methods).
(defprotocol StrConvert
(to-str [this]))
(extend-protocol StrConvert
nil
(to-str [this] "null")
java.lang.Integer
(to-str [this] (str this))
java.lang.String
(to-str [this] (str "\"" this "\""))
clojure.lang.Keyword
(to-str [this] (to-str (name this)))
java.lang.Object
(to-str [this] (str this)))
data
contains a sequence of 10000 random integers, that are randomly converted to String
, nil
, keyword
or vector
.
(let [fns [identity ; as is (integer)
str ; stringify
(fn [_] nil) ; nilify
#(-> % str keyword) ; keywordize
vector] ; vectorize
data (doall (map #(let [f (rand-nth fns)] (f %))
(repeatedly 10000 (partial rand-int 1000000))))]
;; print a summary of what we have in data
(println (map (fn [[k v]] [k (count v)]) (group-by class data)))
;; multimethods
(c/quick-bench (dorun (map convert data)))
;; cond-itionnal
(c/quick-bench (dorun (map convert-cond data)))
;; protocols
(c/quick-bench (dorun (map to-str data))))
The result is for data
containing :
([clojure.lang.PersistentVector 1999] [clojure.lang.Keyword 1949]
[java.lang.Integer 2021] [java.lang.String 2069] [nil 1962])
- Multimethods: 6.26 ms
- Cond-itionnal: 5.18 ms
- Protocols: 6.04 ms
I would certainly suggest as @DanielCompton : the design matters more than the pure performances that seem on pair for each method, at least on this example.
回答2:
Multimethods allow for open extension; others can extend your multimethod dispatching on arbitrary expressions by adding new defmethod
s in their source. Cond expressions are closed to extension by others or even your own code without editing the cond
source.
If you just want to act on conditional logic then a cond is the way to go. If you're wanting to do more complex dispatching, or apply a function over many types of data with different behaviour then a multimethod is probably more appropriate.
回答3:
Why worry when you can measure?
Here is a benchmark sample using criterium library. Cond
and Multi-methods
codes are taken from http://blog.8thlight.com/myles-megyesi/2012/04/26/polymorphism-in-clojure.html.
Caveat This is just a sample on doing benchmark comparing multimethod
and cond
performance. The result below--that shows cond
performs better than multimethod
, can not be generalized to various usage in practice. You can use this benchmarking method to your own code.
;; cond (defn convert-cond [data] (cond (nil? data) "null" (string? data) (str "\"" data "\"") (keyword? data) (convert-cond (name data)) :else (str data))) (bench (convert-cond "yolo")) Evaluation count : 437830380 in 60 samples of 7297173 calls. Execution time mean : 134.822430 ns Execution time std-deviation : 1.134226 ns Execution time lower quantile : 133.066750 ns ( 2.5%) Execution time upper quantile : 137.077603 ns (97.5%) Overhead used : 1.893383 ns Found 2 outliers in 60 samples (3.3333 %) low-severe 2 (3.3333 %) Variance from outliers : 1.6389 % Variance is slightly inflated by outliers ;; multimethod (defmulti convert class) (defmethod convert clojure.lang.Keyword [data] (convert (name data))) (defmethod convert java.lang.String [data] (str "\"" data "\"")) (defmethod convert nil [data] "null") (defmethod convert :default [data] (str data)) (bench (convert "yolo")) Evaluation count : 340091760 in 60 samples of 5668196 calls. Execution time mean : 174.225558 ns Execution time std-deviation : 1.824118 ns Execution time lower quantile : 170.841203 ns ( 2.5%) Execution time upper quantile : 177.465794 ns (97.5%) Overhead used : 1.893383 ns nil
来源:https://stackoverflow.com/questions/28577115/performance-of-multimethod-vs-cond-in-clojure