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创建的reducer直接调用-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、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

这仍然是一种不好的时间处理方法,你在记录范围和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}"}uya}。

接着,我在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源代码更为相似。而1-redux补丁在精神上与ClojureScript源代码更为相似。

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

0票数

评论由:dnolen

如果基准测试能解释补丁前后性能的变化,那么它们将更有信息量。

0票数

评论者:jdevuyst

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

我没有推荐补丁的一个原因是,我不知道您想要优化哪个用例。

...