最近在进行性能工作时,我发现对于少数几个参数,将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元素持久哈希表(未展开)| 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 RAM)上运行,使用ubuntu 12.04和一个Java 8的最新版本。附件中包含了cpuinfo
、uname
和java -version
的输出。
启用了相对标准的JVM生产标志,并且注意关闭了leiningen的启动时间优化(这将禁用许多JIT优化)。
可以通过克隆存储库并运行script/bench
来运行基准测试。
关于这个补丁有一个悬而未决的问题:我们应该将调用展开到什么程度?update
(在1.7预览版本中被展开)展开为3个参数。添加更多的展开并不困难,但这会影响assoc的可读性。
补丁:CLJ-1656-v5.patch