Performance of multimethod vs cond in Clojure
多方法比协议慢,即使使用多方法可以提供更灵活的解决方案,也应该在协议可以解决问题时尝试使用协议。
那么
多种方法允许开放扩展;其他人可以通过在源代码中添加新的
如果您只想对条件逻辑进行操作,那么cond是可行的方法。如果您想进行更复杂的调度,或者对具有不同行为的多种类型的数据应用函数,那么多方法可能更合适。
为什么要测量时会担心?
这里是使用标准库的基准样本。
注意事项这只是比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | ;; 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 |
为了跟进@AlexMiller的评论,我尝试使用更多随机数据进行基准测试,并添加了协议实现(还向其他方法添加了另一种类型-Integer)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | (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))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (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)))) |
结果适用于包含:
的
1 2 | ([clojure.lang.PersistentVector 1999] [clojure.lang.Keyword 1949] [java.lang.Integer 2021] [java.lang.String 2069] [nil 1962]) |
- 多种方法:6.26毫秒
- 条件:5.18毫秒
- 协议:6.04毫秒
我肯定会建议使用@DanielCompton:设计要比每种方法成对出现的纯性能更重要,至少在此示例中如此。