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 毫秒"

包含?集合
"经过时间:153.092212 毫秒"

映射的速度仍然比集合快两倍用于查找,并且实现集合的 ILookup 比 contains? 更快。

1 答案

+2
by
被选中 by
 
最佳回答

从基础的ILookup问题说起,ILookup是对在关联数据结构中根据键查找值的一种抽象。在Clojure中,集合不是关联数据结构(尽管它们是在映射的基础上实现的,有时呈现为k->k的映射,即 get)。因此,根据我对抽象的理解,将ILookup扩展到集合上是没有意义的。同样,关键字调用是对关联查找进行优化的。检查集合是否包含值的推荐函数是 contains?

by
"ILookup是对在关联数据结构中根据键查找值的一种抽象" - 如果我们将集合视为函数,难道它们不是通过实现关联转换就能被视为足够的吗?函数最终都是映射(至少纯函数是这样的,如集合)。此外,有一个独立的(并实现)ILookup的“关联”接口,因此可能使集合具有与映射相同的性能,因为它们是值闭列表中的查找首选结构。感谢您的回答。我还编辑了我的答案,以显示`contains?`仍然没有使集合具有与映射或甚至“ILookup”表集合的性能,这对我来说非常令人失望。
...