此条目涉及两个(独立但相似)关于使 apply 更快的主意
- 避免多次遍历序列
- 避免在 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