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)))

结果是:

关键词集合
"耗时:283.526096毫秒"

关键词查找集合
"耗时:121.766786毫秒"

关键词映射
"耗时:70.514017毫秒"

集合contains?
"耗时:153.092212毫秒"

对于查找,映射仍然比集合快X2倍,而且为集合实现ILookup比contains?更快。

1 个答案

+2

被选中
 
最佳答案

从关于 ILookup 的基本问题开始。ILookup 是一种关于在关联数据结构中查找键的值的抽象。在 Clojure 中,集合不是关联数据结构(即使它们是在映射周围实现的,并且在某些情况下表现为 k->k 的映射,即 get)。因此,根据我对抽象的理解,没有必要基于集合扩展 ILookup。同样,关键字调用是为了关联查找而优化的。检查集合是否包含值的最佳函数是 contains?

"ILookup 是关于在关联数据结构中查找键的值的抽象" - 如果我们将集合视为函数,难道不是足够将它们视为实现关联转换吗?函数最终是映射(至少是纯函数,如集合)。此外,还有一个单独的“关联”接口,它(并且实现了)ILookup,因此也许有必要将集合的性能与映射相匹配,仅因为它们是 AFAICT 闭值列表中查找的首选结构。感谢你的回答。我还编辑了我的答案以显示 `contains?` 仍然无法将集合的性能提升到与映射相当甚至可以实现 ILookup 的集合,(在我看来)这在许多常见用例中是非常令人失望的。
...