评论者:jdevuyst
嗯。我想,惰性序列确实会导致大量的分配。
好吧,我重写了我的测试,并且又运行了几次。现在我也在矢量和数组上进行了测试。
补丁1需要一点调整。在调用coll-fold时,补丁1只为类型{{object}}指定了回退(即调用r/reduce)。我不得不为类型{{array}}添加相同的回退。(这很奇怪!)
所以以下是结果。
对于矢量
`
(let [coll (vec (repeat 100 (vec (range 100))))]
(time (dotimes [n 3000]
(->> coll
(r/mapcat identity)
(r/map inc)
(r/filter even?)
(r/fold +)))))
`
补丁1: 205872毫秒
补丁2: 210756毫秒
对于数组
`
(let [coll (into-array (repeat 100 (into-array (range 100))))]
(time (dotimes [n 3000]
(->> coll
(r/mapcat identity)
(r/map inc)
(r/filter even?)
(r/fold +)))))
`
补丁1: 123567毫秒
补丁2: 119704毫秒
我运行了几次测试,结果相对稳定。补丁1对矢量来说更快,补丁2对数组来说更快。
这是有意义的。
在补丁1中,reducer将直接调用{-reduce}。在补丁2中,reducer首先调用r/reduce,它会根据集合是矢量还是数组调用{-reduce}或array-reduce。因此,在矢量的情况下,补丁2包含一个额外的函数调用,但在数组的情况下则避免了在本地类型上调用协议方法。
使用宏(或复制粘贴)可以避免额外的函数调用。这值得尝试吗,或者更重要的是保持代码整洁?
--
我刚刚意识到补丁2在语义上略微不同于Clojure所做的,尽管这可能是Clojure的一个错误:https://groups.google.com/forum/#!searchin/clojure-dev/kv-reduce/clojure-dev/bEqECvbExGo/iW4B2vEUh8sJ。我建议使用宏(或复制粘贴)以避免补丁2中的额外函数调用,这也可以解决这个差异。