2015-02-18 3 views
0

Мультиметоды медленнее протоколов, и следует попытаться использовать протоколы, когда они могут решить проблему, хотя использование мультиметодов дает более гибкое решение. Так что же происходит с cond и мультиметодом? Они могут быть использованы для решения одной и той же проблемы, но я предполагаю, что у мультиметода есть огромная служебная нагрузка против cond. Если да, то почему я хотел бы использовать multimethod вместо cond?Производительность multimethod vs cond в Clojure

ответ

2

Чтобы следить за @AlexMiller комментарий, я попытался бенчмарка с большим количеством рандомизированных данных и суммируется реализацию протокола (также добавил еще один тип - Integer - для различных методов).

(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 содержит последовательность случайных чисел 10000, которые случайным образом преобразованы в String, nil, keyword или 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)))) 

В результате для data, содержащий:

([clojure.lang.PersistentVector 1999] [clojure.lang.Keyword 1949] 
[java.lang.Integer 2021] [java.lang.String 2069] [nil 1962]) 
  • Мультиметоды: 6,26 мс
  • Cond-itionnal: 5,18 мс
  • Протоколы: 6.04 мс

Я несомненно, предложили бы @DanielCo mpton: дизайн имеет большее значение, чем чистые характеристики, кажущиеся на пару для каждого метода, по крайней мере, на этом примере.

6

Мультиметоды допускают открытое расширение; другие могут распространять вашу многоточную диспетчеризацию на произвольные выражения. Выражения Cond закрыты для расширения другими или даже вашим собственным кодом.

Если вы просто хотите действовать на условной логике, то конд - это путь. Если вы хотите выполнять более сложную диспетчеризацию или применять функцию по многим типам данных с различным поведением, то, вероятно, более целесообразным будет использовать многометод.

+0

Но сколько накладных расходов вводит во многих вариантах vs cond? – user3139545

+0

Согласен. Используйте тот подход, который лучше всего подходит для вашей конкретной задачи, и беспокоиться о производительности позже. – Alex

6

Зачем беспокоиться, когда вы можете измерить?

Вот эталонный образец с использованием библиотеки criterium. Cond и Multi-methods коды взяты с http://blog.8thlight.com/myles-megyesi/2012/04/26/polymorphism-in-clojure.html.

Caveat Это просто пример делать тест сравнения multimethod и cond производительности. Результат ниже - то, что показывает cond, работает лучше, чем multimethod, не может быть обобщен для различного использования на практике. Вы можете использовать этот метод бенчмаркинга для своего собственного кода.

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

+6

Вы не должны доверять этому эталону - вы только конвертируете один тип, что приводит к оптимальному пути кода в обоих случаях (сайт мономорфного вызова в мультиметоде). Лучше протестировать с помощью рандомизированной смеси типов ввода (идеально подходящей для вашего реального кода). –

Смежные вопросы