2024年Clojure状态调查中分享您的想法!

欢迎!请查看关于页面,了解更多该如何工作的信息。

0
Clojure
当对数组的{{reduce}}调用进行分块序列处理时,相比当前的{{dotimes}},整体性能有明显提升。受影响的函数有{{map}}、{{filter}}和{{keep}}。以下表格显示了相关的基准测试结果(单位:毫秒)。方括号中的数字比原始值慢(尽管幅度很小)。长范围的总体改进最为明显。

*长范围*
|| f                    || before(doall)|| after(doall)|| before(chunk-last)|| after(chunk-last)||
 | {{(map inc lr)}}      | 3.75          | 2.88         | 2.15               | 2.06              |
 | {{(keep identity lr)}}| 2.56          | 2.16         | 0.75               | 0.72              |
 | {{(filter odd? lr)}}  | 2.77          | 2.20         | 1.53               | 1.45              |

*范围*
|| f                    || before(doall)|| after(doall)|| before(chunk-last)|| after(chunk-last)||
 | {{(map inc lr)}}      | 3.64          | [3.70]       | 2.32               | [2.50]           |
 | {{(keep identity lr)}}| 2.10          | 1.94         | 0.56               | 0.46              |
 | {{(filter odd? lr)}}  | 1.95          | [1.99]       | 1.19               | [1.66]           |

*数组*
|| f                    || before(doall)|| after(doall)|| before(chunk-last)|| after(chunk-last)||
 | {{(map inc lr)}}      | 3.81          | 3.68         | 2.44               | 2.15              |
 | {{(keep identity lr)}}| 2.03          | [2.16]       | 0.53               | 0.46              |
 | {{(filter odd? lr)}}  | 2.08          | [2.82]       | 1.67               | 1.39              |

*gvec*
|| f                    || before(doall)|| after(doall)|| before(chunk-last)|| after(chunk-last)||
 | {{(map inc lr)}}      | 3.69          | [3.83]       | 1.46               | 1.35              |
 | {{(保持标识lr)}}| 2.86          | 2.82         | 2.44               | 2.52              |
 | {{(筛选奇数lr)}}  | 2.95          | 2.70         | 2.08               | 2.07              |

所有基准测试均在启动后的JVM上使用"Criterium 'bench'执行,并丢弃具有大离群值差异的结果。使用的通用基准模板格式为:{{(let [xs chunked-seq] (bench (doall (f xs))))}} 其中

* "chunked-seq"是以下之一:{{(range 100000)}}, {{(range 1e5)}}, {{(vec (range 1e5))}} 或 {{(into (vector-of :int) (range 1e5))}}
* "doall"是{{doall}}或{{chunk-last}}(以下定义)。
* "f"是以下之一:{{(map inc xs)}}, {{(filter odd? xs)}}或{{(keep identity xs)}}。

观察

* 块越大,增加越多。除了32个以上块的定制(分块)序列可能还会额外受益。
* 相同的更改使得{{keep-indexed}}和{{map-indexed}}变得更糟,因此它们未被更改。
* {{for}}宏也了解块,但使用显式循环来处理{{:let, :when, :while}}情况,这对于与块缓冲区更改区分很困难。
* {{chunk-last}}是一个了解块的函数,用于按块访问最后一个元素。与(逐个遍历序列的){{doall}}相比,{{chunk-last}}在更改前后代码中都更有效。函数定义如下:{{(defn chunk-last [xs] (when-let [xs (seq xs)] (if-let [cn (chunk-next xs)] (recur cn) (last xs))))}}
* 在{{{map}}}定义之前的前定义 pre-definition of {{dotimes}} 可以从核心中移除。这包含在补丁中。

8个答案

0
by

由 alexmiller 发布的评论

你能压缩补丁吗?

0
by

由 reborg 发布的评论

当然。

0
by

由 alexmiller 发布的评论

谢谢!

0
by

由 reborg 发布的评论

CLJ-2346-2.patch 中的新功能:在看到速度略有提升后,将互操作调用 {{.count}} 改为普通 {{count}}。已更新表格以反映新基准。

0

由 alexmiller 发布的评论

为什么使用 .reduce 而不是 reduce 呢?

0

由 reborg 发布的评论

因为 IChunk 上的 reduce 不是通过正常 coll-reduce 暴露的。

0

由 alexmiller 发布的评论

如果 IChunk 扩展了 IReduceInit,则可以这样做。

0
参考资料:https://clojure.atlassian.net/browse/CLJ-2346(由 reborg 报告)
欢迎使用 Clojure 常见问题解答,您可以在哪里向 Clojure 社区成员提问并获得答案。
...