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

欢迎!有关如何使用本站的相关信息,请参阅关于页面。

0
Clojure

此问题涉及两个(独立但类似)加快 apply 速度的想法

  1. 避免多次遍历序列
  2. 避免在 apply 的 3+ 参数情况下创建序列

这两个问题都 multiply 运行时间,因此如果我们避免创建那个序列并且只小心遍历一次序列,我们可以获得 10x 到 15x 的速度提升。

预告:applyTo 的当前实现可能在实际调用实现之前遍历序列最多三次

`
(defn r-6
([] 1)
([x] x)
([x y] x)
([a b c, d e f] f)
([a b c, d e f & xs] f))

(apply r-6 1 2 3 [4 5 6])
`

首先构建序列,在 RestFn.applyTo 中遍历一次,调度到 AFn.applyToHelper,再次遍历以获取参数数量。最后遍历一次以获取参数并调用实现。

此代码的运行时间约为 310 纳秒,添加补丁后,运行速度提高 10 倍至约 30 纳秒。这是一个构造示例,尽管不是不现实的 apply 使用。所有其他的性能改进从 1x 到 15x。似乎没有性能下降。

这种方法(以及许多其他方式)可以通过仅遍历一次序列来优化:只需在遍历序列时检查 nil 或获取 RequiredArity。

至于(2),我需要添加一个新协议,以避免与只 reifying IFn 的人产生向下兼容性问题。对于 3+ 个参数的 apply,我们执行 instanceof 检查并直接调度到接口,而不是构建中间序列。

PS:类似的 CLJS 问题:https://dev.clojure.org/jira/browse/CLJS-2099

2 个答案

0

评论者:aralo

生成的补丁代码: https://github.com/rauhs/clj-bench/blob/master/src/clj_bench/apply.clj

所以我认为审查只需要检查边缘情况,而不必每行代码都查一遍。基准测试很难。我需要一些指导。尽管如此,我在任何情况下都没有看到性能回归。

我发现了什么:基准测试应该同时使用 RestFn 和 AFn 对象调用 {{apply}},因为只用一种类型的对象调用它会让 JVM 对这种情况进行优化并提供一些不切实际的数量。

0
参考资料:https://clojure.atlassian.net/browse/CLJ-2319(由 aralo 报告)
...