在最近进行性能工作的时候,我发现将assoc解展开为单个调用比使用多个键要快得多(在我的特定应用中大约10%)。Zachary Tellman随后指出,clojure.core根本不会对关联进行解展开,即使是对于相对较低数量的键。
我们已经有了解展开其他通过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元素持久化哈希表(未解展开) | 190ns | 313ns | 551ns | 651ns |
| 20元素持久化哈希表(解展开assoc) | N/A | 250ns | 433ns | 524ns |
| | | | | |
| 记录(未解展开) | 12ns | 72ns | 105ns | 182ns |
| 记录(解展开assoc) | 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个参数。添加更多的解展开并不困难,但这会影响assoc的可读性。
补丁: CLJ-1656-v5.patch