2024 Clojure 调查问卷!中分享您的想法。

欢迎!请参阅关于页面以获取更多信息。

+9
Clojure

最近在进行性能工作时,我发现将多个操作合并成一个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内存)上运行,使用ubuntu 12.04和Java 8的最新版本。附带的文件包括cpuinfounamejava -version

启用了相对标准的JVM生产标志,并采取措施禁用leiningen的启动时间优化(这会导致禁用许多JIT优化)。

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

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

补丁: CLJ-1656-v5.patch

25 回答

0
by

评论由:alexmiller 发表

是的,通过 var 调用内联有效。

0
by

评论由:tcrayford 发表

Michael,

那个组只是上传了带有你打补丁的 Clojure master 版本,使用与之前相同的方式构建(你应该能够检出回购并复制)。

0
by

评论由:alexmiller 发表

补丁 CLJ-1656-v5.patch 似乎在 core.clj 的大约 179 行与旧的 assoc 版本没有任何操作?

新的版本需要包含 arglists 和其他类似的东西。我不确定那里的宏/私有变量也是否如此。你是否尝试将 RT.assocN() 与向量配合使用?

测试套件中是否有针对 assoc 和 N 对的现有测试?

0
by

评论由:michaelblume 发表

clojure.core 中的依赖关系是这样的,assoc 需要在语法引用之前很好地定义,所以我只是让它两次定义,一次更慢,一次更快。我会提交一个带有 arglists 的补丁。每个新可变参数是否都需要 arglist,或者现有的 arglist 足够?(我担心我对 arglists 元数据的了解不到100%)assoc 的现有测试相当令人烦恼。我在我的树中有生成性测试,因为这比为所有不同的可变参数编写用例更有趣。如果它看起来有用,可以考虑铺贴它,但它可能是一种过度设计。

0
by

评论由:michaelblume 发表

这就是我之前提到的测试补丁,比我想象的还要过头

0
by

评论由:michaelblume 发表

好了,代码和测试。

这还检查在 varargs 情况下,assoc! 接收了偶数数量的 kvs,这是 assoc 的行为。测试验证了 assoc 和 assoc! 在奇数参数数量时都会抛出异常。

0

评论由:alexmiller 发表

现有的 arglist 没问题,只是用于文档目的覆盖了生成的那个。

你尝试过 RT.assocN() 的东西吗?

我想问的另一个问题是,人们实际上是否经常这样做,以至于这很重要? :)

0

评论由:michaelblume 发表

已更新补丁以应用到 master

0

评论由:alexmiller 发表

这仍然需要评估不同计数出现的频率。根据一些非常快速的检查,传递两个 kvs 足够常见,以至于很重要。传递三个远不如那么常见,以此类推。但我希望看到一些从运行一些东西中得到的粗略频率想法。我认为当前的展开太多(2-3个 kvs 可能就足够了)。

0
参考: https://clojure.atlassian.net/browse/CLJ-1656(由 tcrayford 提出)
...