我是looking at 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"
这是多方法的自然结果还是这里有其他问题?
答案 0 :(得分:4)
Clojure multimethods允许基于任意调度函数的运行时多态行为。这对于构建临时层次结构和抽象非常强大,但是您为这种灵活性付出了性能损失。您可能希望使用协议重新实现您的解决方案。只有在需要完整的运行时类型灵活性时才使用多方法。
答案 1 :(得分:3)
一般来说任何做得更多的事都会花费更多时间。因为多方法提供了多种方式来发送,所以它们需要的时间比我必须回答的问题要长“是的,与协议相比”。
在实践中从协议开始并在必要时转到多方法(看起来你需要它们)。 您无法使用代码加快计算机速度,但可以减少代码
答案 2 :(得分:0)
我认为你的多方法实现较慢的原因也可能是因为你在一个懒惰序列(由范围提供)上使用reduce而不是在clojure.core中使用的递增索引上使用loop / recur。尝试将clojure.core / re-groups实现的循环部分复制到第二个defmethod中,看看是否不会提高性能。
很难说没有看到你的测试用例,但如果匹配器中有很多组,你可能会花费更多的时间将组减少为向量而不是多方法调度。如果虽然有很少的组,那么多方法调度将花费更多的时间。在任何一种情况下,具有相同的实现将消除时序差异的潜在影响因素,并帮助您更好地感受多方法调度中的实际开销。
您可能会考虑的另一件事是减速可能是由于反思。尝试将*warn-on-reflection*设置为true,看看是否有抱怨。也许另一种战略类型提示可能有所帮助。