这是为了跟随我在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?更快。