以下是超级快.
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a))
Run Code Online (Sandbox Code Playgroud)
但如果添加偏,那么就是这么慢.代码返回的结果应该是一样的,对吧?为什么性能差异如此之大?
(let [a (atom {})]
(doall (map #(swap! a (partial merge {% 1})) (range 10000))) (println @a))
Run Code Online (Sandbox Code Playgroud)
noi*_*ith 17
(partial f a)而#(f a %)实际上是相当不同的.
无论定义如何f,都可以为部分应用的函数提供任意数量的参数,运行时会将它们放在一个列表中并用于apply获取结果.因此,无论如何,每次使用构造的函数时,都会构建一个短命列表partial.另一方面,#()创建一个新类,如果你使用一个将permgen与常规堆分离的旧JVM,那么当你为类使用越来越多的专用内存时,这可能会成为一个问题.
即使@noisesmith 的答案是正确的,性能问题也不是来自partial. 问题更微不足道:这只是参数传递给 的顺序merge。
在#(swap! a merge {% 1})atom中作为第一个参数传递给merge. 在每一步中,只有{% 1}与原子生长图相结合。
在 中#(swap! a (partial merge {% 1})),原子作为第二个参数传递给 ,merge并且在每一步中原子的所有元素都a连接到{% 1}。
merge'让我们尝试使用该调用进行测试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
Run Code Online (Sandbox Code Playgroud)
merge与的性能(partial merge')具有可比性。(partial merge)实际上很糟糕。