在最近进行性能工作时,我发现将展开后的单个 assoc 调用与其他多个键的调用相比,速度显著更快(对于我的特定应用程序而言大约有 10%)。Zachary Tellman 之后指出,clojure.core 官方实际上根本不会展开 assoc,即使是对于相对较少的键的情况。
我们已经在其他性能关键函数中对通过 apply 调用的功能进行了展开,例如 update
(详见 https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L5914),但是 assoc
(我认为它在众多应用程序和库的关键路径上),可能会从这个操作中受益。
我尚未开发此补丁,但我进行了一些独立的基准测试工作
https://github.com/yeller/unrolling-assoc-benchmarks
基准测试结果
代码: https://github.com/yeller/unrolling-assoc-benchmarks/blob/master/src/bench_assoc_unrolling.clj
| |1 |2 |3 |4 |
| :-- | :-- | :-- | :-- | :-- |
| 空数组映射(未展开) | 23ns | 93ns | 156ns | 224ns |
| 空数组映射(展开 assoc) | N/A | 51ns | 80ns | 110ns |
| | | | | |
| 20 元素持久化 hashmap(未展开) | 190ns | 313ns | 551ns | 651ns |
| 20 元素持久化 hashmap(展开 assoc) | N/A | 250ns | 433ns | 524ns |
| | | | | |
| 记录(未展开) | 12ns | 72ns | 105ns | 182ns |
| 记录(展开 assoc) | N/A | 21ns | 28ns | 41ns |
每个测量都是在单独的 JVM 中进行的,以避免 JIT 路径依赖。
基准测试是在一个普通服务器上进行的(8 个 CPU,32gb RAM),运行 ubuntu 12.04 和最新的 Java 8 版本。附带的文件包含 cpuinfo
、uname
和 java -version
输出。
启用了相对标准的 JVM 生产标志,并已采取措施禁用 leiningen 的启动时间优化(这禁用了许多 JIT 优化)。
通过克隆存储库并运行 script/bench
来运行基准测试。
关于这个补丁还有一个悬而未决的问题:我们应该展开这些调用的范围有多远?update
(在 1.7 测试版本中展开)展开到 3 个参数。增加更多展开并不复杂,但这会影响 assoc 的可读性。
补丁: CLJ-1656-v5.patch