2024年Clojure调查!分享您的想法。

欢迎!请参阅关于页面了解有关此功能的更多信息。

0
ClojureScript
  1. 当前这种情况无法工作

(->> nil

 (r/map identity)
 (r/reduce + 0))
 ; org.mozilla.javascript.JavaScriptException: Error: No protocol method IReduce.-reduce defined for type null

这是因为由r/reducer或r/folder创建的reducers直接调用-reduce(IReduce)。因此,它们绕过了函数r/reduce中对nil的特殊情况。

  1. 类型为数组的集合也存在一个完全相似的问题。

  2. 补丁CLJS-700错误地将coll-fold定义为teams.core/IPersistentVector类型。这应该是teams.core/PersistentVector。 (在ClojureScript中没有协议IPersistentVector。)

我将很快附上一个补丁,通过实现nil和数组的IReduce来解决上述所有问题。该补丁还包括单元测试。

18 答案

0

评论者:jdevuyst

其他补丁,其中r/reduce和r/fold将数组和nil视为特殊案例 -- 相反,数组和nil实现IReduce和CollFold。

r/reducer、r/folder和r/Cat的协议方法现在调用r/reduce和r/fold,而不是直接调用-reduce和coll-fold。

此补丁还修复了Cat中coll-fold实现的错误,之前它使用了(reducef)作为初始值而不是(combinef)。新代码是从Clojure实现中复制粘贴的,并使用fork-join空白。

0
_评论者:dnolen_

第二个补丁中,{{implements?}}可能应该是{{satisfies?}}。您在这之前/之后修改补丁运行过基准测试吗?
0
_评论由:jdevuyst_提出

如果我的理解正确的话,那么 {{(satisfies? x y)}} 大致等同于 {{(or (implements? x y) (natively-satisfies? x y))}}。

如果原生类型(nil、数组、对象当前情况下)被视为特殊情况,那么 {{implements?}} 似乎更合适。

{{satisfies?}} 也可以使用,因此我附上了新的 'alt' 补丁。
0

评论者:jdevuyst

第一个补丁在实际运行以下代码时实际上更快

(time (->> (repeat 1000 (vec (range 1000)))
           vec
           (r/mapcat identity)
           (r/map inc)
           (r/filter even?)
           (r/fold +)))

这大约需要 700 毫秒。使用第一个补丁可以终止加速 100-300 毫秒。这是经过重复(但非正式)测试的结果。

我想担忧的是,第一个补丁可能会因为涉及到扩展 nil、数组、对象类型而使其他随机代码放缓。但我也不确定我应该测试什么。

(注意,第二个和第三个补丁也包含对 {{Cat}} 的修复,并包括更多的单元测试。建议不要直接应用第一个补丁。)

0

评论由:dnolen

是的,你测试了太多的东西,包括 {{vec}}、{{range}}、懒序列。还有,测试一个小 N。看看 Mori README 上的 reducers 示例 - https://github.com/swannodette/mori。谢谢。

0
_评论由:jdevuyst_提出

我尝试运行以下代码:


(let [coll (vec (repeat 1000 (vec (range 10))))]
  (time (doseq [n (range 1000)]
               (->> coll
                    (r/mapcat identity)
                    (r/map inc)
                    (r/filter even?)
                    (r/fold +)))))


我得到的一些最后结果如下:

第一个补丁:75680 毫秒
第二个补丁:76585 毫秒

说实话,尽管第一个补丁在大多数情况下似乎占上风,但有时第二个补丁更快。

我还试了另一件事,那就是从第二个补丁中删除 {{implements?}}/{{satisfies?}} 检查,并替代为覆盖 {{object}} 类型的 protocol 方法 coll-fold(就像第一个补丁里的那样)。这种 '混合' 方法通常(但不总是)会导致速度放缓。

我不确定我应该如何进行。我可能需要同时运行两个补丁几分钟吗?
0

评论由:dnolen

这仍然是一种做时间记录的坏方法,你记录了范围和序列化的成本。请使用 {{dotimes}}。

0

评论者:jdevuyst

Hm. 我想这会导致大量分配。

好的,我已经重写了我的测试并运行了几次。现在我也在矢量和数组上进行了测试。

补丁1需要稍作调整。当调用 coll-fold 时,补丁1只指定了对 {{object}} 类型的回退(即调用 r/reduce)。我不得不为 {{array}} 类型添加相同的回退。(这很奇怪!)

以下是结果。

对于矢量

`
(let [coll (vec (repeat 100 (vec (range 100))))]
(time (dotimes [n 3000]

      (->> coll
          (r/mapcat identity)
          (r/map inc)
          (r/filter even?)
          (r/fold +)))))

`

补丁1:205872 毫秒
补丁2:210756 毫秒

对于数组

`
(let [coll (into-array (repeat 100 (into-array (range 100))))]
(time (dotimes [n 3000]

      (->> coll
          (r/mapcat identity)
          (r/map inc)
          (r/filter even?)
          (r/fold +)))))

`

补丁1:123567 毫秒
补丁2:119704 毫秒

我多次运行了我的测试,结果相当一致。补丁1对矢量更快,补丁2对数组更快。

这是有道理的。

在补丁1中,reducer 直接调用 {{-reduce}}。在补丁2中,reducer 首先调用 r/reduce,如果收集的是矢量,则调用 {{-reduce}};如果它是数组,则调用 {{array-reduce}}。因此,对于矢量,补丁2包含额外的函数调用,但对于数组,它避免了在本地类型上调用协议方法。

使用宏(或复制粘贴)可以避免额外的函数调用。这值得尝试吗,还是保持代码清洁更重要?

--

我意识到,补丁2在语义上略不同于 Clojure 的做法,尽管这可能是在 Clojure 中的一个错误:[链接](https://groups.google.com/forum/#!searchin/clojure-dev/kv-reduce/clojure-dev/bEqECvbExGo/iW4B2vEUh8sJ)。我建议使用宏(或复制粘贴)来避免补丁2中的额外函数调用,这也可以解决这种差异。

0

评论由:dnolen

你是如何进行基准测试的?使用 V8?JavaScriptCore?SpiderMonkey?在浏览器中?什么优化设置等。

0

评论者:jdevuyst

我使用了 repljs (Rhino?)。我将在明天在一个更现实的环境中进行测试。

0

评论由:dnolen

是的,使用Rhino进行基准测试并不具有参考价值。

0

评论者:jdevuyst

我使用{{cljs}}和{{"{:optimizations :advanced}"}},编译了相同的代码(n=3000)。

然后在Firefox、Chrome和Safari的最新稳定版本中对它进行了测试。我关闭了所有浏览器。对于每个浏览器,我遵循以下程序

  • 打开浏览器
  • 打开开发者控制台
  • 运行补丁1的基准测试
  • 运行补丁2的基准测试
  • 运行补丁1的基准测试并写下结果
  • 运行补丁2的基准测试并写下结果
  • 关闭浏览器

Firefox
- 补丁1. 向量:26057毫秒
- 补丁1. 数组:25026毫秒
- 补丁2. 向量:26258毫秒
- 补丁2. 数组:36653毫秒
- 总结:补丁1对向量和数组更快

Chrome
- 补丁1. 向量:7804毫秒
- 补丁1. 数组:7092毫秒
- 补丁2. 向量:7754毫秒
- 补丁2. 数组:6768毫秒
- 总结:补丁2对向量和数组更快

Safari
- 补丁1. 向量:167230毫秒
- 补丁1. 数组:108780毫秒
- 补丁2. 向量:173940毫秒
- 补丁2. 数组:110012毫秒
- 总结:补丁1对向量和数组更快

我不太清楚这代表什么。

0

评论者:jdevuyst

我附上了第一个补丁的新版本。

此补丁修复了{{r/Cat}}的问题。(第二个和第三个补丁也解决了这个问题。包括单元测试。)

此补丁还修复了数组的{{r/fold}}。

总的来说,需要在以下补丁之间做出选择。

  • CLJS-736-patch-1-redux.patch
  • CLJS-736-alt.patch(使用implements?)/ CLJS-alt-satisfies.patch(使用satisfies?)

如上所述,alt补丁在精神上与Clojure源代码更相似。alt补丁在精神上与ClojureScript源代码更相似。

如上所述,alt补丁在语义上与原始Clojure源代码略有不同——但它不清楚哪种行为是“正确”的。

0

评论由:dnolen

如果基准测试解释了补丁前后的性能,则会更有参考价值。

0

评论者:jdevuyst

{{r/reduce}}之前在nil或JavaScript数组上不起作用。

我不太推荐补丁的一个原因是,我不知道您想优化什么样的用例。

...