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

欢迎!请参阅关于页面了解有关此工作的一些更多信息。

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创建的reducer直接调用-reduce(IReduce)。因此,它绕过了r/reduce函数中对nil的特殊情况。

  1. 对于类型为array的集合也存在一个完全类似的问题。

  2. 补丁CLJS-700错误地将coll-fold定义为类型cljs.core/IPersistentVector。这应该是cljs.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,array,object当前)被视为特殊情况,那么似乎 {{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、array和object类型。不过,我不太确定我应该测试什么。

(注意,第二个和第三个补丁也包括了{{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}}的协议方法coll-fold,这种方法一般(但并不总是)会导致速度变慢。

我不确定该怎么办。也许我应该同时运行这两个补丁几分钟?
0

评论由:dnolen

这仍然是一种不合适的计时方式,你正在记录range和seq'ing的开销。使用{{dotimes}}。

0

由:jdevuyst 评论

嗯。我想惰性序列确实导致了大量的分配。

好的,我重写了我的测试并运行了多次。现在我还在向量和数组上进行了测试。

补丁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}}编译了相同的代码(n=3000),并通过{{"{:optimizations :advanced}"} Mixer}}。

然后,我在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源代码略有不同。而 patch-1-redux 与Clojure源代码更为相似。

正如上述所说,alt补丁在使用上与原始Clojure源代码略有差异——但是不清楚哪种行为是正确的。

0

评论由:dnolen

如果基准测试能够说明补丁前后的性能变化将更有参考价值。

0

由:jdevuyst 评论

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

我难以推荐补丁的原因之一是我不知道你希望优化哪种场景的使用。

...