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

欢迎!请参阅关于页面以了解更多关于这是如何工作的信息。

0
Clojure

此条目涉及两个(独立但相似)关于使 apply 更快的主意

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

这两个问题都会增加运行时间,因此如果我们避免创建该序列并且只遍历一次,就可以获得 10x 至 15x 的速度提升。

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

`
(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 纳秒,打好补丁后大约 30 纳秒,速度提高 10x。这是一个虽然构造实例,但不是不合理的 apply 使用情况。所有其他性能改进从 1x 到 15x 不等。似乎没有出现任何性能下降。

可以通过仅遍历一次序列来实现这一点:当遍历 first/next 时,简单地检查 nil 或 getRequiredArity。

至于(2),我需要添加一个新的协议,以免向仅重用 IFn 的人破坏向后兼容性。在 apply 中对于 3+ 个参数,我们执行了一次 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

因此,我认为审查只需检查边缘情况,而不必逐行检查代码。基准测试很困难。我需要一些指导。不过,我在任何情况下都没有看到性能下降。

我发现:基准测试应该同时调用 {{apply}} 的 RestFn 和 AFn 对象,因为只调用其中一个类型会使 JVM 优化此情况并提供一些不切实际的数字。

0
参考: https://clojure.atlassian.net/browse/CLJ-2319(由 aralo 报告)
欢迎使用 Clojure Q&A,您可以在这里提出问题并从 Clojure 社区的成员那里获得答案。
...