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定义给了类型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、数组、对象目前)被视为特殊情况,那么似乎 {{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}} 的协议方法 coll-fold(就像第一个补丁中做的那样)。这种'混合'方法通常(但并非总是)会导致减慢。

我不确定我接下来应该怎么做。我是否应该同时运行这两个补丁几分钟?
0

_由 dnolen 发布的评论:

这仍然是一个做时间测量的糟糕方法,你记录了范围和 seq 的成本。请使用 {{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}}和{{":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/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数组。

我难以推荐一个补丁的一个原因是我不知道您希望对哪一种用例进行优化。

...