为什么部分在 clojure 中这么慢
Why partial is so slow in clojure
下面超级快
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a))
但是如果添加partial,那就太慢了。代码的结果return应该是一样的吧?为什么性能差异如此之大?
(let [a (atom {})]
(doall (map #(swap! a (partial merge {% 1})) (range 10000))) (println @a))
(partial f a)
和#(f a %)
其实差别很大
无论 f
的定义如何,您都可以向部分应用的函数提供任意数量的参数,运行时会将它们放在一个列表中并使用 apply
来获取结果。因此,无论如何,每次使用由 partial
构造的函数时,都会构造一个短暂的列表。另一方面,#()
创建了一个新的 class,如果您使用旧的 JVM 将 permgen 与常规堆隔离开来,这可能会成为一个问题,因为您使用了越来越多的专用内存classes.
即使@noisesmith 的回答是正确的,性能问题确实 而不是 来自 partial
。
问题更微不足道:它只是参数传递给 merge
的顺序。
在#(swap! a merge {% 1})
中,原子作为第一个参数传递给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
merge
和(partial merge')
的表现不相上下。 (partial merge)
实际上很糟糕。
下面超级快
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a))
但是如果添加partial,那就太慢了。代码的结果return应该是一样的吧?为什么性能差异如此之大?
(let [a (atom {})]
(doall (map #(swap! a (partial merge {% 1})) (range 10000))) (println @a))
(partial f a)
和#(f a %)
其实差别很大
无论 f
的定义如何,您都可以向部分应用的函数提供任意数量的参数,运行时会将它们放在一个列表中并使用 apply
来获取结果。因此,无论如何,每次使用由 partial
构造的函数时,都会构造一个短暂的列表。另一方面,#()
创建了一个新的 class,如果您使用旧的 JVM 将 permgen 与常规堆隔离开来,这可能会成为一个问题,因为您使用了越来越多的专用内存classes.
即使@noisesmith 的回答是正确的,性能问题确实 而不是 来自 partial
。
问题更微不足道:它只是参数传递给 merge
的顺序。
在#(swap! a merge {% 1})
中,原子作为第一个参数传递给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
merge
和(partial merge')
的表现不相上下。 (partial merge)
实际上很糟糕。