2024 Clojure状态调查中分享您的想法!

欢迎!请查看关于页面以了解更多关于如何使用本站的信息。

+9
Clojure

最近我在做性能工作时发现,对于单次关联调用进行展开要比使用多个键要快得多(在我的特定应用中约为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内存),使用ubuntu 12.04和Java 8的最新版本。附带的文件包括cpuinfounamejava -version输出。

启用了一些相对标准的JVM生产标志,并特别注意禁用了leiningen的启动时间优化(这会禁用许多JIT优化)。

可以通过克隆存储库并运行script/bench来运行基准测试

关于这个补丁有一个待解决的问题:我们应该展开这些调用到什么程度?update(在1.7 alpha中已展开)已展开到3个参数。增加展开并不困难,但这会影响到assoc的可读性。

补丁:CLJ-1656-v5.patch

25 答案

0 投票

由 alexmiller 发表的评论:

是的,内联通过变量引用来实现。

0 投票

由 tcrayford 发表的评论:

Michael,

该组只是一个应用了您的补丁的clojure master上传版本,以与之前相同的方式进行构建(您应该能够检出存储库并进行复制)。

0 投票

由 alexmiller 发表的评论:

补丁 CLJ-1656-v5.patch 似乎没有对旧版本的 assoc 做任何处理(在 core.clj 的第 179 行附近)?

新版本需要具有 arglists 以及其他类似内容。我不太确定那里的宏/私有变量。您尝试利用 RT.assocN() 和一个向量了吗?

测试套件中是否有针对具有 N 对关联的 assoc 的现有测试?

0 投票

由 michaelblume 发表的评论:

clojure.core 中的依赖性使得 assoc 需要在 syntax-quoting 之前就很好地定义,所以我只是让它定义了两次,一次慢一点,一次快一点。我会提交一个具有 arglists 的补丁。对于每个新的参数数目是否需要 arglist,或者现有的 arglists 够用?(我恐怕我并不完全清楚 arglists 元数据的含义)现在的 assoc 还缺少一些现有测试。我在树上有一个生成性测试,因为它似乎比为所有不同的参数编写用例更有趣。如果它看起来有用,它可能有点过分了,我可以发布它。

0 投票

由 michaelblume 发表的评论:

这是我所提到的测试补丁,甚至比我记忆中更过分

0 投票

由 michaelblume 发表的评论:

那里的代码和测试。

这也检查在 varargs 情境下 assoc! 是否传递了偶数个 kvs,这是 assoc 的行为。测试验证了 assoc 和 assoc! 在奇数参数数量时都会抛出异常。

0 投票

由 alexmiller 发表的评论:

现有的参数列表很好 - 它只是覆盖了生成的那个以文档目的。

您尝试过 RT.assocN() 的任何东西吗?

我想问的另一个问题是,人们是否真的这样做足够多以至于有影响? :)

0 投票

由 michaelblume 发表的评论:

更新补丁以应用于 master

0 投票

由 alexmiller 发表的评论:

这仍然需要评估不同数量的频率。根据一些非常快速的检查,传递两个 kvs 的情况足够常见,会有所影响。传递三个的情况远不如常见,以此类推。但是我很想看到一些关于运行一些东西后的粗略频率想法。我认为当前的展开太多(2-3 个 kvs 可能就足够了)。

0 投票
参考:https://clojure.atlassian.net/browse/CLJ-1656(由 tcrayford 报告)
...