2024 年 Clojure 状态调查中分享您的想法!

欢迎!请参阅关于页面以获取更多关于如何运作的信息。

0
Collections

c.c/bash 总是使用 hashCode 来处理 Java 集合,这与 Clojure 集合中使用 Murmur3 相冲突。

user=> (== (hash (java.util.ArrayList. [1 2 3])) (hash [1 2 3])) false user=> (= (java.util.ArrayList. [1 2 3]) [1 2 3]) true

一种修复方法是向 Util/hasheq 为 java.util.Collections 添加一个特殊案例,就像它现在对 String 的处理一样。

有关此主题的 Clojure 群组讨论链接:https://groups.google.com/forum/#!topic/clojure/dQhdwZsyIEw

43 回答

0

由 stu 发表评论

我认为这需要更多的考虑,并且不应阻碍 1.6。

0

由 jafingerhut 发表评论

截至 2014 年 3 月 20 日的最新 Clojure master,这两个补丁 clj-1372.diff 和 clj-1372-2.diff 无法干净地应用。我相信在 2014 年 3 月 19 日的提交之前,它们可以干净地应用,唯一的问题似乎是一个 changed line of diff context 的更改。鉴于关于是否希望进行此类更改的讨论,看起来在决定是否需要做出更改之前还需要更多的思考。

0

由 mikera 发表评论

这是一个相当严重的缺陷。它绝对需要修复。这实际上并不是关于混合使用Clojure和Java集合是否是一个可能的使用场景(它可能不是...),而是关于提供人们可以依赖的一致保证。

例如,我现在真的不确定我拥有的使用集合或映射的库函数是否损坏。我特别担心任何基于哈希值的对象缓存/记忆化/内联实现的代码。这样的代码现在可能存在一些非常令人讨厌的细微缺陷。

由于它们是库函数,我无法保证传入的对象类型,所以代码必须与所有可能的输入一起工作(或者我需要编写清晰的文档字符串,并在输入不受支持时抛出异常)。

0
_评论由:michalmarczyk_发布

此补丁(0001-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M.patch)使hasheq与java.util.{List,Map,Map.Entry,Set}中的=保持一致。此外,它将特殊处理String(返回hashCode的hasheq)扩展到所有未处理的类型(下面将对此进行注释)。

它也在这里有

https://github.com/michalmarczyk/clojure/tree/alien-hasheq-2

早期版本在这里有

https://github.com/michalmarczyk/clojure/tree/alien-hasheq

如果我的理解正确,需要基准测试的主要是给定的Clojure类型在clojure.lang.Util/hasheq中的“分派时间”。因此,我反复对相同的持久性哈希映射进行哈希操作,以Criterium基准的形式进行测试,理论上是测量IHashEq实例上的分派时间。然后我运行了一个单独的基准测试,该测试对PHM、字符串和长整型进行哈希并在uncheck-add中累加结果。希望这是一个好的开始,我确信还有更多的基准测试会有用。

结果对我来说有些令人惊讶:在本构建中,PHM上的hasheq实际上比1.6.0快一点;在1.6.0上,“添加三个hasheq”基准略快。


;;; 1.6.0

注意。j.u.HM基准不相关
> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (clojure.lang.Util/hasheq phm)) (c/bench (clojure.lang.Util/hasheq juhm)))
警告:最后的GC需要1.24405836928592 %的运行时间
评估次数:5549560980,在60个样本的92492683次调用中。
             平均执行时间:9.229881纳秒
    标准偏差:0.156716纳秒
   下四分位数:8.985994纳秒(2.5%)
   上四分位数:9.574039纳秒(97.5%)
                   使用的开销:1.741068纳秒

在60个样本中发现了2个异常值(3.3333%)
    低严重      2 (3.3333%)
 异常值方差:6.2652 % 异常值略微增加了方差
评估次数:35647680,在60个样本的594128次调用中。
             平均执行时间:1.695145微秒
   标准偏差:20.186554纳秒
   下四分位数:1.670049微秒(2.5%)
   上四分位数:1.740329微秒(97.5%)
                   使用的开销:1.741068纳秒

在60个样本中发现了2个异常值(3.3333%)
    低严重      2 (3.3333%)
 异常值方差:1.6389 % 异常值略微增加了方差
nil

用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (unchecked-add (clojure.lang.Util/hasheq phm) (unchecked-add (clojure.lang.Util/hasheq "foo") (clojure.lang.Util/hasheq 123)))))
警告:最后垃圾收集所需的运行时间为1.028614538339401 %
评估次数:在60个样本中调用17165805次,总计1029948300次。
             平均执行时间:56.797488纳秒
    执行时间标准差:0.732221纳秒
   执行时间下四分位数:55.856731纳秒(2.5%)
   执行时间上四分位数:58.469940纳秒(97.5%)
                   开销使用:1.836671纳秒

在60个样本中发现了1个异常值(1.6667%)
    低严重等级     1(1.6667%)
 异常值方差:1.6389 % 异常值略微增加了方差
nil

;;; 补丁已应用

用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] (assert (= (hash phm) (hash juhm))) (c/bench (clojure.lang.Util/hasheq phm)) (c/bench (clojure.lang.Util/hasheq juhm)))
评估次数:在60个样本中调用92294978次,总计5537698680次。
             平均执行时间:8.973200纳秒
    执行时间标准差:0.157079纳秒
   执行时间下四分位数:8.733544纳秒(2.5%)
   执行时间上四分位数:9.289350纳秒(97.5%)
                   开销使用:1.744772纳秒
评估次数:在60个样本中调用41360次,总计2481600次。
             平均执行时间:24.287800微秒
    执行时间标准差:288.124326纳秒
   执行时间下四分位数:23.856445微秒(2.5%)
   执行时间上四分位数:24.774097微秒(97.5%)
                   开销使用:1.744772纳秒
nil

用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (unchecked-add (clojure.lang.Util/hasheq phm) (unchecked-add (clojure.lang.Util/hasheq "foo") (clojure.lang.Util/hasheq 123)))))
警告:最后垃圾收集所需的运行时间为1.298136122909759 %
评估次数:在60个样本中调用15912525次,总计954751500次。
             平均执行时间:61.681794纳秒
    执行时间标准差:0.712110纳秒
   执行时间下四分位数:60.622003纳秒(2.5%)
   执行时间上四分位数:62.904801纳秒(97.5%)
                   开销使用:1.744772纳秒

在60个样本中发现了1个异常值(1.6667%)
    低严重等级     1(1.6667%)
 异常值方差:1.6389 % 异常值略微增加了方差
nil


顺便提一下,其他分支上可用的较早版本的补丁没有为String单独的分支。这使得实现了IHashEq的对象的hasheq更快,但将“三个hash”基准测试的速度降低了大约2倍。
0

评论者:alexmiller

为了清楚起见,请按名称引用此处附上的补丁,这样随着时间的推移,我们就不必将附件时间与评论时间关联起来。

我对实现IHashEq的东西的成本并不特别担心,因为它们除了潜在的内联问题外不应受到影响。我对落入所有检查成本的对象的hasheq成本很感兴趣。上面的评论中列出的列表是一个好的起点 - 比如Class、Character和Var(这可能在Var中得到解决)。

0

评论由:michalmarczyk 发布

很好的建议,我已经编辑了上面的评论,包括了补丁名称。

感谢性能比较的建议--我将在大约6分钟后发布一些新结果。

0

评论由:michalmarczyk 发布

首先,为了完整起见,这里是一个新的补丁(0001-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M-alternative.patch),它对未处理的类型不进行额外的大量嘟囔。对于单个PHM情况,速度较慢;以下为详细信息。另外,这里有GitHub上的分支

https://github.com/michalmarczyk/clojure/tree/alien-hasheq-3

至于新的结果,绩效损失相当大,我担心

`
;;; 与补丁(murmur hashCode默认版本)
user=> (let [class-instance java.lang.String character-instance \a var-instance #'hash] (c/bench (clojure.lang.Util/hasheq class-instance)) (c/bench (clojure.lang.Util/hasheq character-instance)) (c/bench (clojure.lang.Util/hasheq var-instance)))
警告:最终的GC需要运行时的1.409118084170768%。
评估计数:655363680次,60个样本,每次10922728个调用。

         Execution time mean : 96.459888 ns
Execution time std-deviation : 1.019817 ns

执行时间下四分位数:95.079086纳秒(2.5%)
执行时间上四分位数:98.684168纳秒(97.5%)

               Overhead used : 1.708347 ns

评估计数:675919140次,60个样本,每次11265319个调用。

         Execution time mean : 88.965959 ns
Execution time std-deviation : 0.825226 ns

执行时间下四分位数:87.817159纳秒(2.5%)
执行时间上四分位数:90.755688纳秒(97.5%)

               Overhead used : 1.708347 ns

评估计数:574987680次,60个样本,每次9583128个调用。

         Execution time mean : 103.881498 ns
Execution time std-deviation : 1.103615 ns

执行时间下四分位数:102.257474纳秒(2.5%)
执行时间上四分位数:106.071144纳秒(97.5%)

               Overhead used : 1.708347 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil

;;; 1.6.0
user=> (let [class-instance java.lang.String character-instance \a var-instance #'hash] (c/bench (clojure.lang.Util/hasheq class-instance)) (c/bench (clojure.lang.Util/hasheq character-instance)) (c/bench (clojure.lang.Util/hasheq var-instance)))
警告:最终的GC需要运行时的1.3353133083866688%。
评估计数:1829305260次,60个样本,每次30488421个调用。

         Execution time mean : 34.205701 ns
Execution time std-deviation : 0.379106 ns

执行时间下四分位数:33.680636纳秒(2.5%)
执行时间上四分位数:34.990138纳秒(97.5%)

               Overhead used : 1.718257 ns

在60个样本中发现了2个异常值(3.3333%)

low-severe	 1 (1.6667 %)
low-mild	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
评估计数:1858100340次,60个样本,每次30968339个调用。

         Execution time mean : 30.401309 ns
Execution time std-deviation : 0.213878 ns

执行时间下四分位数:30.095976纳秒(2.5%)
执行时间上四分位数:30.871497纳秒(97.5%)

               Overhead used : 1.718257 ns

评估计数:1592932200次,60个样本,每次26548870个调用。

         Execution time mean : 36.292934 ns
Execution time std-deviation : 0.333512 ns

执行时间下四分位数:35.795063纳秒(2.5%)
执行时间上四分位数:36.918183纳秒(97.5%)

               Overhead used : 1.718257 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil
`

带有一个PHM和新的补丁的Class/Character/Var结果(默认情况下没有额外的murmur步骤)

`
用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (unchecked-add (clojure.lang.Util/hasheq phm) (unchecked-add (clojure.lang.Util/hasheq "foo") (clojure.lang.Util/hasheq 123)))))
警告:最终的GC需要运行时的1.258952964663877%。
评估计数:1007768460次,60个样本,每次16796141个调用。

         Execution time mean : 58.195608 ns
Execution time std-deviation : 0.482804 ns

执行时间下四分位数:57.655857纳秒(2.5%)
执行时间上四分位数:59.154655纳秒(97.5%)

               Overhead used : 1.567532 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil
user=> (let [class-instance java.lang.String character-instance \a var-instance #'hash] (c/bench (clojure.lang.Util/hasheq class-instance)) (c/bench (clojure.lang.Util/hasheq character-instance)) (c/bench (clojure.lang.Util/hasheq var-instance)))
评估计数:647944080次,60个样本,每次10799068个调用。

         Execution time mean : 91.275863 ns
Execution time std-deviation : 0.659943 ns

执行时间下四分位数:90.330980纳秒(2.5%)
执行时间上四分位数:92.711120纳秒(97.5%)

               Overhead used : 1.567532 ns

评估计数:699506160次,60个样本,每次11658436个调用。

         Execution time mean : 84.564131 ns
Execution time std-deviation : 0.517071 ns

执行时间下四分位数:83.765607纳秒(2.5%)
执行时间上四分位数:85.569206纳秒(97.5%)

               Overhead used : 1.567532 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
评估计数:594919980次,60个样本,每次9915333个调用。

         Execution time mean : 100.336792 ns
Execution time std-deviation : 0.811312 ns

执行时间下四分位数:99.313490纳秒(2.5%)
执行时间上四分位数:102.167675纳秒(97.5%)

               Overhead used : 1.567532 ns

在60个样本中发现了3个异常值(5.0000%)

low-severe	 3 (5.0000 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil
`

0

评论由:michalmarczyk 发布

这里有一个新的补丁(0001-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M-substring.patch),它采取了激进的策略,将可迭代的/Map/Entry测试替换为在类名上使用.startsWith("java.util.")。 (我尝试了.getClass().getPackage(),但性能太差了。) 分支在这里

https://github.com/michalmarczyk/clojure/tree/alien-hasheq-4

这个补丁在“穿帮”情况下的哈希性能似乎非常好

`
user=> (let [class-instance java.lang.String character-instance \a var-instance #'hash] (c/bench (clojure.lang.Util/hasheq class-instance)) (c/bench (clojure.lang.Util/hasheq character-instance)) (c/bench (clojure.lang.Util/hasheq var-instance)))
警告:最终GC占用运行时1.31690036780011 %
评估计数:在60个样本中的27690894次调用中为1661453640。

         Execution time mean : 35.099750 ns
Execution time std-deviation : 0.422800 ns

执行时间下四分位数:34.454839 ns (2.5%)
执行时间上四分位数:35.953584 ns (97.5%)

               Overhead used : 1.556642 ns

评估计数:在60个样本中的27169460次调用中为1630167600。

         Execution time mean : 35.487409 ns
Execution time std-deviation : 0.309872 ns

执行时间下四分位数:35.083030 ns (2.5%)
执行时间上四分位数:36.190015 ns (97.5%)

               Overhead used : 1.556642 ns

在60个样本中发现了4个异常值(6.6667 %)

low-severe	 3 (5.0000 %)
low-mild	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
评估计数:在60个样本中的24007245次调用中为1440434700。

         Execution time mean : 40.894457 ns
Execution time std-deviation : 0.529510 ns

执行时间下四分位数:40.055991 ns (2.5%)
执行时间上四分位数:41.990985 ns (97.5%)

               Overhead used : 1.556642 ns

nil
`

0
_评论由:michalmarczyk_发布

新的补丁 (...-substring.patch) 为除了List、Map、Map.Entry和Set之外的java.util.**类返回hashCode,当然,所以在这方面没有行为变化。

以下是重复PHM查找的基准测试(比1.6.0稍慢,但速度快在1 ns之内)和“添加三个hasheq”基准测试(带补丁为66 ns,不带为57 ns)


用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] (assert (= (hash phm) (hash juhm))) (c/bench (clojure.lang.Util/hasheq phm)) (c/bench (clojure.lang.Util/hasheq juhm)))
评估计数:在60个样本中的86497354次调用中为5183841240。
             执行时间平均值:10.076893 ns
    执行时间标准差:0.182592 ns
   执行时间下四分位数:9.838456 ns (2.5%)
   执行时间上四分位数:10.481086 ns (97.5%)
                   开销使用:1.565749 ns
评估计数:在60个样本中的51507次调用中为3090420。
             执行时间平均值:19.596627 µs
    执行时间标准差:224.380257 ns
   执行时间下四分位数:19.288347 µs (2.5%)
   执行时间上四分位数:20.085620 µs (97.5%)
                   开销使用:1.565749 ns
nil

用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (unchecked-add (clojure.lang.Util/hasheq phm) (unchecked-add (clojure.lang.Util/hasheq "foo") (clojure.lang.Util/hasheq 123)))))
警告:最终GC占用运行时1.418253438197936 %
评估计数:在60个样本中的14653515次调用中为879210900。
             执行时间平均值:66.939309 ns
    执行时间标准差:0.747984 ns
   执行时间下四分位数:65.667310 ns (2.5%)
   执行时间上四分位数:68.155046 ns (97.5%)
                   开销使用:1.724002 ns
nil


需要注意的是,在测试1.6.0版本时,我已经在新JVM上得到了“三个hasheq”基准的no-patch结果,所以这也是我应用补丁后重复基准测试的方式。对多种不同类型的哈希操作会显著改变结果——可能是HotSpot在看到多个不同类型传入hasheq后放弃了某些优化?
0

评论由:michalmarczyk 发布

以下是一个新的补丁(0005-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M.patch),它引入了一个名为isAlien的静态方法,用于检查实例是否为Map/Map.Entry/Iterable,并使用此方法来测试“非本土集合”。

初始基准测试结果是令人鼓舞的

`
;;;“穿级”基准测试
user=> (let [class-instance java.lang.String character-instance \a var-instance #'hash] (c/bench (clojure.lang.Util/hasheq class-instance)) (c/bench (clojure.lang.Util/hasheq character-instance)) (c/bench (clojure.lang.Util/hasheq var-instance)))
警告:最终的GC(垃圾回收)需要1.258979068087473%的运行时间
评估次数:159,843,210,000次,在60个样本中,每次样本调用26,640,535次。

         Execution time mean : 36.358882 ns
Execution time std-deviation : 0.566925 ns

执行时间下四分位数:35.718889纳秒(2.5%)
执行时间上四分位数:37.414722纳秒(97.5%)

               Overhead used : 1.823120 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
评估次数:1,626,362,446,0次,在60个样本中,每次样本调用27,106,041次。

         Execution time mean : 35.426993 ns
Execution time std-deviation : 0.294517 ns

执行时间下四分位数:35.047064纳秒(2.5%)
执行时间上四分位数:36.058667纳秒(97.5%)

               Overhead used : 1.823120 ns

在60个样本中发现了1个异常值(1.6667%)

low-severe	 1 (1.6667 %)

与异常值方差:1.6389%,方差略受异常值膨胀
评估次数:1,461,423,180次,在60个样本中,每次样本调用24,357,053次。

         Execution time mean : 39.541873 ns
Execution time std-deviation : 0.423707 ns

执行时间下四分位数:38.943560纳秒(2.5%)
执行时间上四分位数:40.499433纳秒(97.5%)

               Overhead used : 1.823120 ns

在60个样本中发现了2个异常值(3.3333%)

low-severe	 2 (3.3333 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil

;;;“三个hasheq”基准测试
用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] #_(assert (= (hash phm) (hash juhm))) (c/bench (unchecked-add (clojure.lang.Util/hasheq phm) (unchecked-add (clojure.lang.Util/hasheq "foo") (clojure.lang.Util/hasheq 123)))))
警告:最终的GC需要1.5536755331464491%的运行时间
评估次数:820,376,460次,在60个样本中,每次样本调用13,672,941次。

         Execution time mean : 71.999365 ns
Execution time std-deviation : 0.746588 ns

执行时间下四分位数:70.869739纳秒(2.5%)
执行时间上四分位数:73.565908纳秒(97.5%)

               Overhead used : 1.738155 ns

在60个样本中发现了2个异常值(3.3333%)

low-severe	 2 (3.3333 %)

与异常值方差:1.6389%,方差略受异常值膨胀
nil
`

0
_评论由:michalmarczyk_发布

啊,我遗漏了上面基准测试中的重复phm hasheq查找+ java.util.HashMap实例对基准测试——现在为完整性呈现此内容(没有惊喜)


用户=> (let [phm (apply hash-map (interleave (range 128) (range 128))) juhm (java.util.HashMap. phm)] (assert (= (hash phm) (hash juhm))) (c/bench (clojure.lang.Util/hasheq phm)) (c/bench (clojure.lang.Util/hasheq juhm)))
警告:最终的GC需要1.260853406580491%的运行时间
评估次数:53,691,357,600次,在60个样本中,每次样本调用89,485,596次。
             执行时间平均值:10.380464纳秒
    执行时间标准差:3.407284纳秒
   执行时间下四分位数:9.510624纳秒(2.5%)
   执行时间上四分位数:11.461485纳秒(97.5%)
                   开销使用:1.566301纳秒

在60个样本中发现了5个异常值(8.3333%)
    低-严重        3(5.0000%)
    低-轻微        2(3.3333%)
  异常值与偏离:96.4408% 偏差因异常值严重膨胀
评估次数:30,781,800次,在60个样本中,每次样本调用51,303次。
             执行时间平均值:19.717981微秒
    执行时间标准差:209.896848纳秒
   执行时间下四分位数:19.401811微秒(2.5%)
   执行时间上四分位数:20.180163微秒(97.5%)
                   开销使用:1.566301纳秒

在60个样本中发现了2个异常值(3.3333%)
    低严重      2 (3.3333%)
 异常值方差:1.6389 % 异常值略微增加了方差
nil
0

评论者:alexmiller

请不要提交任何修改哈希码的补丁,除非是为了使 Java 集合与 Clojure 集合相匹配 - 以及其他的更改都不在这个条目的范围之内。

一般来说,我更希望现在只提供执行时间的平均值报告,而不是所有内容 - 完整的标准输出使得这些评论难以阅读和比较。

0

评论者:alexmiller

能否总结一下方法,并提供一组一致测试的 1.6.0 版本与每个补丁的对比时间 - 例如 Long、PHM、juHM、Class 和 "三个哈希 eq" 测试的哈希时间?

0
_评论由:richhickey_ 添加

"哈希许多不同的类型会明显改变结果 - 想必 HotSpot 在看到传递给 hasheq 的几个不同类型后会退缩一些优化?"

是的 - 如果你的基准测试没有将此站点视为变形的 megamorphic,你会得到各种扭曲的结果。
0
_评论由:michalmarczyk_发布

好的,我认为这是一个改进的微基准测试:对一个长整型、一个双精度浮点数、一个字符串、一个类、一个字符和一个 PHM(单个实例,因此它将是一个哈希查找)的 hasheq 的异或。结果并不令人鼓舞。

一个包含 {{require}} 的单一形式,以便便于运行;还包括一个 {{j.u.HashMap}}(128 个条目)的 hasheq 基准测试。


(do
  (require '[criterium.core :as c])
  (let [l    41235125123
        d    123.456
        s    "asdf;lkjh"
        k    BigInteger
        c    \S
        phm  (apply hash-map (interleave (range 128) (range 128)))
        juhm (java.util.HashMap. phm)
        f    (fn f []
               (-> (clojure.lang.Util/hasheq l)
                   (bit-xor (clojure.lang.Util/hasheq d))
                   (bit-xor (clojure.lang.Util/hasheq s))
                   (bit-xor (clojure.lang.Util/hasheq k))
                   (bit-xor (clojure.lang.Util/hasheq c))
                   (bit-xor (clojure.lang.Util/hasheq phm))))]
    (c/bench (f))
    (c/bench (hash juhm))))


由Criterium报告的平均执行时间

||版本||xor (ns)||j.u.HM (µs)||
|未修补 1.6.0|148.128748|1.701640|
|0005 补丁|272.039667|21.201178|
|原始补丁|268.670316|21.169436|
|-替代补丁|271.747043|20.755397|

子串补丁已损坏(见下文),因此我跳过了它。我描述为“原始”的补丁已附上,文件名为0001-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M.patch。

所有补丁都做出的决定

1. 在默认返回值上方{{hasheq}}中额外的一个{{if}}语句,包含一个三方{{instanceof}}检查。

2. 测试的类型是{{j.u.Iterable}}、{{j.u.Map.Entry}}和{{j.u.Map}}。

{{Murmur3.hashOrdered}}接受{{Iterable}},这就是为什么它在列表上。{{Map}}不扩展{{Iterable}},所以它被单独列出。{{Map.Entry}}在列表上,因为最终散列集合的方式是迭代它们的条目并散列。

实际散列“外星”/宿主类型是通过一个单独的静态方法完成的 -- {{clojure.lang.Util.doalienhasheq}} -- 理论上这允许{{hasheq}}更多地内联,并限制最差的性能冲击到外星集合。

{{doalienhasheq}}检查{{Map}}、{{Map.Entry}}、{{Set}}和{{List}};条目转换为列表进行散列,映射通过条目集进行散列,列表和集合直接传递给{{Murmur3}}。

还有一个默认情况用于其他{{Iterable}}类型 -- 我们必须为这些返回{{hashCode}}或通过与其他{{hashCode}}组合的函数的结果,因为我们使用{{equals}}来测试它们以判断等价性。

0005 补丁让{{hasheq}}调用一个单独的私有静态方法来执行三方类型检查,而其他则直接在真实的{{if}}测试中执行检查。替代补丁和0005补丁在默认情况下返回{{hashCode}},而原始补丁将{{Murmur3.hashInt}}与{{hashCode}}组合。

子串补丁仅适用于{{java.util.**}}类,因此没有解决这个问题(例如,它无法正确散列Guava集合)。

所有补丁都改变了{{c.l.Util.hasheq}}并添加了一个或两个新的静态方法到{{clojure.lang.Util}},作为{{hasheq}}的助手。它们都没有更改其他任何内容。哈希hashCode是一个性能实验,似乎对一些“快速案例”有一些轻微的积极影响(实际上,它仍然是上面提供的微基准测试中当前三个补丁中表现最好的,尽管获胜的差距当然极小)。因此,我认为所有当前补丁实际上都限于直接与票据相关的更改;替代补丁和0005补丁肯定如此。
...