请在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 (of IReduce)。因此绕过了函数r/reduce中nil的特殊情况。

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

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

我将很快附加一个补丁来解决上述所有问题,通过为nil和array实现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
by
_注释由:jdevuyst_做出

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

如果将原生类型(目前为nil、array、object)视为特殊情况,那么{{implements?}}似乎更合适。

然而,{{satisfies?}}也可以,所以我附上了一个新的"alt"补丁。
0
by

评论由: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
by

注释由:dnolen

您的计时太快了,包括{{vec}}、{{range}}、lazy sequences。也测试了小的N。看看Mori的README中的reducers示例 - https://github.com/swannodette/mori。谢谢。

0
by
_注释由: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?}}检查,并覆盖了协议方法coll-fold用于{{object}}类型,而不是像(如在第一个补丁中)那样。这种“混合”方法通常(但并非总是)会导致性能下降。

我不确定我应该怎么着手。也许我应该同时运行这两个补丁几分钟?
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的一个bug: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}"}

然后我在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?)

如上所述,patch-1-redux的实现细节与Clojure源代码在精神上更为相似。alt补丁在精神上与ClojureScript源代码更为相似。

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

0

注释由:dnolen

如果基准测试说明了补丁前后的性能,那么它们将更有信息性。

0

评论由:jdevuyst

{{r/reduce}}曾经对于nil或者JavaScript数组不能正常工作。

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

...