7

我正在查看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" 

这是多方法的自然结果还是这里有其他问题?

4

3 回答 3

4

Clojure 多方法允许基于任意调度函数的运行时多态行为。这对于构建临时层次结构和抽象非常强大,但您会为这种灵活性付出代价。您可能希望使用协议重新实现您的解决方案。只有在需要完全的运行时类型灵活性时才使用多方法。

于 2011-08-31T17:34:36.357 回答
3

一般来说,任何做得更多的事情都会花费更多的时间。因为多方法提供了多种调度方式,它们将比协议花费更长的时间,所以我必须回答您的问题“是的,与协议相比”。

在实践中,从协议开始,在必要时使用多方法(看起来你需要它们)。你不能用代码让计算机更快,但你可以让它做的更少

于 2011-08-31T21:17:16.483 回答
0

我认为您的多方法实现较慢的原因也可能是因为您在惰性序列(由范围提供)上使用 reduce,而不是在 clojure.core 中使用的递增索引上使用循环/递归。尝试将 clojure.core/re-groups 实现的循环部分复制到您的第二个 defmethod 中,看看这是否不会提高性能。

没有看到你的测试用例很难说,但如果匹配器中有很多组,你可能会花费更多的时间将组减少为向量而不是多方法调度。如果组很少,那么多方法调度将是花费时间的更重要部分。在任何一种情况下,具有相同的实现都将消除导致时间差异的潜在因素,并帮助您更好地了解多方法调度中的实际开销。

您可能会考虑的另一件事是放缓可能是由于反思。尝试将*warn-on-reflection*设置为 true,看看它是否抱怨。也许另一种战略类型提示可能会有所帮助。

于 2011-09-01T09:09:15.353 回答