此问题涉及两个(独立但类似)加快 apply 速度的想法
- 避免多次遍历序列
- 避免在 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