2024 Clojure 状态调查!中分享你的想法。

欢迎!请参阅关于页面了解有关此操作的一些更多信息。

+1
Clojure
编辑

这是为了回应我给 StackOverflow 上的一个回答(我希望这是大部分正确的,但我不够经验丰富,不能确信)。有关此问题,那里有比在这个问题中更多的详细信息。

概括来说,(:k my-set) 比 (my-set :k) 和 (:k my-map) 慢得多。这非常反直观,因为我保留了一些我想重复查询成员资格的项目,我把它们保留在集合中。把它们保存在映射中始终是性能更高的选择(映射在两种调用的形式中都运行良好)。

我发现延迟差异的原因是,调用集合的 invoke 比调用键控的 invoke 快得多,后者进行了大量委托和 instanceof 检查。

通过使用 proxy 实现 ILookup,我能够提高 {:k my-set} 的性能。

(def uids #{:a :b :c :d :e :f :g :h :i :j :k :l :m :n :o :p :a1 :b1 :c1 :d1 :e1 :f1 :h1 :i1 :j1 :k1 :l1 :m1 :n1 :o1 :p1})
(def uids-map (into {} (for [k uids] [k k])))
(def lookupable-set (proxy [clojure.lang.APersistentSet clojure.lang.ILookup] [uids-map]
                      (valAt [k] (get uids-map k))))

;; verify
(instance? clojure.lang.APersistentSet lookupable-set) ;; true
(instance? clojure.lang.ILookup lookupable-set) ;; true

(time (dotimes [i 1000000] (:o1 uids))) ;; 134.703101 msecs
(time (dotimes [i 1000000] (:o1 lookupable-set))) ;; 63.187353 msecs  <-- faster
(time (dotimes [i 1000000] (:o1 uids-map))) ;; 35.802762 msecs <-- still fastest

我想知道为什么 clojure 的集合最初不实现 ILookup?搜索不是人们希望从集合中期望做的很大一部分吗?他们已经有了完成这些功能所需的所有适当函数。如果他们只实现了 ILookup,那会破坏什么?或者还有其他不这么做的原因?

谢谢。

编辑

根据 @alexmiller 在评论中的建议,我还重新测试了 (contains? uids :o1),它的速度也比原始的 ILookup 实现慢。

(println "kw set")
(time (dotimes [i 1000000] (:o1 uids)))

(println "kw lookupable set")
(time (dotimes [i 1000000] (:o1 lookupable-set)))

(println "kw map")
(time (dotimes [i 1000000] (:o1 uids-map)))

(println "contains? set")
(time (dotimes [i 1000000] (contains? uids :o1)))

它给了我以下结果:

kw set
"消耗时间: 283.526096 毫秒"

kw lookupable set
"消耗时间: 121.766786 毫秒"

kw map
"消耗时间: 70.514017 毫秒"

contains? set
"消耗时间: 153.092212 毫秒"

与集合相比,映射仍然是搜索的 X2 倍快,为集合实现 ILookup 比使用 contains? 更快。

1 个答案

+2
by
selected by
 
最佳回答

从基础的 ILookup 问题看起,ILookup 是针对在关联数据结构中根据键查找值的一种抽象。在 Clojure 中,集合不是关联数据结构(尽管它们是围绕映射实现的,在某种情况下表现为 k→k 的映射,即 get)。因此,根据我对这种抽象的理解,将 ILookup 扩展到集合上是没有意义的。类似地,关键字调用是针对关联查找进行优化的。检查集合中是否包含某个值的推荐函数是 contains?

by
"ILookup 是关于在关联数据结构中查找键的值的抽象" - 如果我们把集合当作函数,它们是否只是实现了关联变换?函数最终都是映射(至少是纯函数,像集合一样)。另外,还有一个“关联”接口(独立并实现了 ILookup),因此也许我们应该使集合的表现与映射一样出色,因为它们是遍历值闭包列表的理想查找结构。感谢您的回答。我还编辑了我的回答,以显示 `contains?` 仍然不能使集合表现得像映射(甚至“可 ILookup 的”集合)一样,这在许多常见用例中令人失望。
...