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

欢迎!请参阅关于页面以了解有关此工作原理的更多信息。

0 投票
Clojure
使用{{reduce}}对{{ArrayChunk}}进行调用,而不是当前的{{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              |
 | {{(filter odd? lr)}}  | 2.95          | 2.70         | 2.08               | 2.07              |

所有基准测试都是在启动一个全新的 JVM 之后,通过排除具有大异常波动的结果来执行的。使用的通用基准模板形式为:{{(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}} 定义前的初始临时预定义的 {{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 投票
参考:[CLJ-2346](https://clojure.atlassian.net/browse/CLJ-2346)(由 reborg 报告)
...