这个工单涉及两个(独立但相似)关于使apply更快的方法
- 避免多次遍历seq
- 避免在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