Замкнутые мультиметоды медленны по своей природе - PullRequest
7 голосов
/ 31 августа 2011

Я смотрел , функция clojure.core перегруппировывается:

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

И подумал, что было бы "лучше" переписать ее как мультиметод:

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

И все же при сравнении времени, которое я ожидал увидеть, переписывание происходит в 4 раза медленнее:

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

Это естественный результат мультиметодов или здесь что-то не так?

Ответы [ 3 ]

4 голосов
/ 31 августа 2011

Мультиметоды Clojure допускают полиморфное поведение во время выполнения на основе произвольных функций диспетчеризации.Это очень эффективно для построения специальных иерархий и абстракций, но вы платите удар по производительности за такую ​​гибкость.Возможно, вы захотите повторно реализовать свое решение с помощью протокола.Используйте мультиметоды только тогда, когда вам нужна полная гибкость типов времени выполнения.

3 голосов
/ 01 сентября 2011

в общем все, что делает больше, займет больше времени .Так как мультиметоды предлагают различные способы отправки, они потребуют больше времени, чем протоколы, поэтому я должен ответить на ваш вопрос «да, по сравнению с протоколами».

На практике начинайте с протоколов и переходите к мультиметодам, когда это необходимо (и, похоже, они вам нужны). вы не можете сделать компьютер быстрее с помощью кода, но вы можете сделать это меньше

0 голосов
/ 01 сентября 2011

Я думаю, что причина того, что ваша мульти-методная реализация медленнее, может также быть связана с тем, что вы используете сокращение для отложенной последовательности (предоставляемой диапазоном) вместо цикла / повторения для возрастающего индекса, который используется в clojure.core. Попробуйте скопировать часть цикла реализации clojure.core / re-groups во второй метод определения и посмотрите, не увеличивает ли это производительность.

Трудно сказать, не увидев тестовый пример, но если в сопоставителе много групп, вы можете потратить гораздо больше времени на приведение групп к вектору, чем при отправке из нескольких методов. Однако, если групп немного, то отправка нескольких методов будет занимать более значительную часть затраченного времени. В любом случае, наличие одной и той же реализации устранит потенциальный фактор, влияющий на разницу во времени, и поможет вам лучше понять фактические издержки при отправке несколькими методами.

Другая вещь, которую вы могли бы рассмотреть, - это вероятность того, что замедление связано с отражением. Попробуйте установить * warn-on-отражением * в true и посмотреть, если он жалуется. Может быть, поможет другой стратегический тип подсказки.

...