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