为什么部分在 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}

让我们用调用 mergemerge' 来测试一下,反转参数。将其他地图的所有元素连接起来的地图是最后一张:

(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) 实际上很糟糕。