评论人: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中的额外函数调用,也可能解决这个问题不一致性。