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

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

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

此补丁还修复了Cat中coll-fold实现的错误,之前它用(reducef)作为初始值,而不是(combinef)。新代码是从Clojure实现中复制粘贴的,并使用fork-join占位符。

0
_评论者:dnolen_

在第二个补丁中,可能应该使用 {{satisfies?}} 而不是 {{implements?}}。您在补丁前后运行了任何基准测试吗?
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 类型而使其他随机代码变慢。不过,我不确定我应该测试哪些具体的问题。

(注意,第 2 个和第 3 个补丁还包含对 {{Cat}} 的修复,并包含更多的单元测试。第一个补丁应首选不直接应用。)

0
by

评论文由:dnolen

是的,你测试的时间太多了,包括 {{vec}}、{{range}}、懒序列。还测试了小 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 +)))))


我得到的一些最后结果如下

第 1 个补丁:75680 毫秒
第 2 个补丁: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的bug:[链接](https://groups.google.com/forum/#!searchin/clojure-dev/kv-reduce/clojure-dev/bEqECvbExGo/iW4B2vEUh8sJ)。我对使用宏(或复制粘贴)的建议,也可以解决这个差异问题。

0

评论文由:dnolen

你是如何进行基准测试的?使用V8?JavaScriptCore?SpiderMonkey?在浏览器中?有哪些优化设置等。

0

评论者:jdevuyst

我使用了repljs(Rhino?)。我将在明天在一个更具现实性的环境中再次进行测试。

0
by

评论文由:dnolen

是的,使用Rhino进行基准测试不是很有信息量。

0
by

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

评论者: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补丁的精神与ClojureScript源码更为相似。

0

评论文由:dnolen

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

0

评论者:jdevuyst

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

我不推荐补丁的一个原因是我不知道您希望优化哪种使用案例。

...