为什么部分是如此缓慢的clojure

时间:2015-01-20 00:58:02

标签: clojure

以下是超级快。

 (let [a (atom {})] 
  (doall (map #(swap! a merge {% 1}) (range 10000))) (println @a))

但如果添加部分,那么就太慢了。代码返回的结果应该是一样的,对吧?为什么表现差异如此之大?

(let [a (atom {})] 
  (doall (map #(swap! a (partial merge {% 1})) (range 10000))) (println @a))

2 个答案:

答案 0 :(得分:17)

(partial f a)#(f a %)实际上完全不同。

无论f的定义如何,您都可以为部分应用的函数提供任意数量的参数,运行时会将它们放入列表中并使用apply来获取结果。所以,无论如何,每次使用由partial构造的函数时,都会构造一个短命列表。另一方面,#()创建了一个新类,如果您使用较旧的JVM将permgen与常规堆隔离,那么当您为类使用越来越多的专用内存时,这可能会成为一个问题。

答案 1 :(得分:2)

即使@noisesmith的回答是正确的, 的性能问题也来自partial。 问题更简单:它只是参数传递给merge的顺序。

#(swap! a merge {% 1})中,原子作为第一个参数传递给merge。在每一步中,只有{% 1}与原子生长图相结合。

#(swap! a (partial merge {% 1}))中,原子作为第二个参数传递给merge,并且在每个步骤中,原子a的所有元素都连接到{% 1}

让我们尝试使用调用merge'的{​​{1}}进行测试,然后反转参数。其他地图中所有元素连接在一起的地图是最后一个:

merge

(defn merge' [& maps] (apply merge (reverse maps))) (require '[criterium.core :as c]) (c/quick-bench (let [a (atom {})] (dorun (map #(swap! a merge {% 1}) (range 10000))) )) => Execution time mean : 4.990763 ms (c/quick-bench (let [a (atom {})] (dorun (map #(swap! a (partial merge' {% 1})) (range 10000))) )) => Execution time mean : 7.168238 ms (c/quick-bench (let [a (atom {})] (dorun (map #(swap! a (partial merge {% 1})) (range 10000))) )) => Execution time mean : 10.610342 sec merge的表现具有可比性。 (partial merge')实际上很糟糕。