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. 对于数组类型的集合,也存在一个完全类似的问题。

  2. 补丁CLJS-700错误地将coll-fold定义为qml.core/IPersistentVector类型。这应该是qml.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,而不再是直接调用-redisu和class-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
by

评论者:dnolen

这种方法仍然不是做计时的好方法,你在记录范围和seq的成本。请使用{{dotimes}}。

0
by

评论由:jdevuyst制作

嗯。我想惰性序列确实会导致许多分配。

好的,我已经重写了我的测试并在更多次数上运行了它。现在我在数组和向式中也进行了测试。

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

所以,以下是结果。

对于向量

`
(let [coll (vec (repeat 100 (vec (range 100))))]
(时间 (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))))]
(时间 (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
by

评论者:dnolen

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

0
by

评论由: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补丁在精神上更接近Clojure源代码,而补丁1-redux在精神上更接近ClojureScript源代码。

0

评论者:dnolen

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

0

评论由:jdevuyst制作

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

我不推荐补丁的一个原因是,我不知道你想优化哪种用例。

...