2024年度Clojure调查中分享你的看法!

欢迎!请查看关于页面获取更多关于这个工作方式的信息。

0
Clojure

这个工单涉及两个(独立但相似)关于使apply更快的方法

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

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

预告:applyTo的当前实现可能会在调用实现之前遍历seq多达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,再次遍历以获取arity。最后一次遍历以获取参数并调用实现。

这个代码大约运行了310ns,使用补丁后,它在约30ns内运行得快10倍。这虽然是一个构建的例子,但不是apply不切实际的用法。所有其他性能改进的幅度从1倍到15倍不等。似乎没有出现性能下降。

可以通过只遍历序列一次来优化(和许多其他方式):只需在遍历序列时检查nil或者获取getRequiredArity即可。

至于(2),我需要添加一个新的协议,以避免与只实现了IFn的用户向后兼容性。在apply的3+参数中,我们进行instanceof检查,并将调度直接到接口而不构建一个中间序列。

附言:类似的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
by
参考: https://clojure.atlassian.net/browse/CLJ-2319(由 aralo 报告)
...