Performance of multimethod vs cond in Clojure

守給你的承諾、 提交于 2020-06-26 12:25:09

问题


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 defmethods 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!