{{refer}} 在 {{require}}、{{use}} 和 {{refer-clojure}} 的情况下是基础,而且在将符号/变量的映射从命名空间复制到另一个命名空间的主要工作岗位上效率并不是特别高。
方法:可以进行的某些改进
- 直接到命名空间映射中,避免创建过滤的中间地图 (ns-publics)
- 使用 transient 构建引用的映射
- 替代一个个新引用的情况,构建所有更改的映射,然后执行 cas
- 对于 (:require :only ...) 的情况 - 只需遍历包含的变量并在其中查找匹配项,而不是遍历所有引用的变量
在所有这些工作中,肯定还有更多的重大变化(如不可变命名空间),这将进一步提高性能,但我尽量使得此次变更的范范围较小。
尽管单个 refer 的时间大大减少(对于 (refer clojure.core) 减少了约 50%,对于 :only 使用减少了约 90%),但 refer 只是更广泛的 require 加载时间的一个小部分,因此实际上的改进是适度的。
性能
| 在 new repl 中 expr | 1.7.0-beta3 | 1.7.0-beta3+补丁 |
| :-- | :-- | :-- | :-- |
| (in-ns 'foo) (clojure.core/refer 'clojure.core) | 2.65 毫秒 | 0.994 毫秒 |
| (in-ns 'bar) (clojure.core/refer 'clojure.core :only '(link: inc dec)) | 1.04 毫秒 | 0.113 毫秒 |
| (use 'criterium.core) | 0.877 毫秒 | 0.762 毫秒 |
| (require '(link: clojure.core.async :refer (>!! <!! chan close!))) | 3408 毫秒 | 3302 毫秒 |
补丁:clj-1730-2.patch
筛选备注
补丁似乎正确,但我们应考虑
- {{if}} 替代 {{when}} 的非习惯用法使分支难以阅读
- {{if}} 的非习惯用法缩进(两个分支都在同一行)难以阅读
- 我认为不应该将未找到的作为 {{IllegalAccessError}},但原始代码已经这样做,所以...
- 围绕 swap 的乐观并发循环 从未 放弃,这是否可行?