评论者: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 msecs
补丁 2: 210756 msecs
对于数组
`
(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 msecs
补丁 2: 119704 msecs
我进行了几次测试,结果相当一致。对于向量,补丁 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 中提出使用宏(或复制粘贴)以避免额外函数调用的建议,也可能修复这个差异。