最近在进行性能工作时,我发现将代码展开为单个关联调用要比使用多个键快得多(对我的特定应用程序而言大约快10%)。然后,Zachary Tellman 指出,clojure.core 在键的数量相对较少的情况下根本不会展开关联。
我们已经展开了其他关键性能函数,例如通过 apply
调用的函数 update
等,例如 update
https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L5914,但关联(我认为在许多应用程序和库的关键路径上),可能会从中受益。
我还没有为这个问题开发补丁,但我进行了一些独立的基准测试
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 |
| 空数组映射(展开关联) | N/A | 51ns | 80ns | 110ns |
| | | | | |
| 20个元素的持久HashMap(未展开) | 190ns | 313ns | 551ns | 651ns |
| 20个元素的持久HashMap(展开关联) | N/A | 250ns | 433ns | 524ns |
| | | | | |
| 记录(未展开) | 12ns | 72ns | 105ns | 182ns |
| 记录(展开关联) | N/A | 21ns | 28ns | 41ns |
每个测量都是在单独的JVM中进行的,以避免JIT路径依赖性。
基准测试是在商品服务器上进行的(8个CPU,32gb内存),使用 ubuntu 12.04 和一个最新的Java 8版本。包含 cpuinfo
, uname
和 java -version
会话输出。
启用了相对标准的JVM生产标志,并且注意禁用了leiningen的启动时间优化(这会禁用许多JIT优化)。
可以通过克隆仓库并运行 script/bench
来运行基准测试。
关于这个修补程序有一个悬而未决的问题:我们应该展开这些调用多远?update
(在1.7 alpha中展开)展开到3个参数。增加更多的展开并不困难,但这会影响关联的可读性。
补丁: CLJ-1656-v5.patch