以下是超级快。
(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))
答案 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')
实际上很糟糕。