评论由: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的一个bug:https://groups.google.com/forum/#!searchin/clojure-dev/kv-reduce/clojure-dev/bEqECvbExGo/iW4B2vEUh8sJ。我建议使用宏(或复制粘贴)来避免补丁2中额外的函数调用,也可能解决这个问题。