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

欢迎!请查阅关于页面获取更多关于如何使用本站的信息。

0
集合

c.c/hash始终使用hashCode对Java集合进行哈希处理,这在与使用Murmur3的Clojure集合进行比较时是不兼容的。

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添加一个特殊案例,就像它现在为Strings做的那样。

有关此主题在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日的提交之前可以干净利落地应用,唯一的问题是更改了diff上下文的行数。鉴于关于是否需要此类更改的讨论,似乎在决定是否需要做出任何更改之前需要更多的思考。

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的特殊处理扩展到所有其他未处理类型(关于此的评论见下文)。

它也在这里可用

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

早期版本在这里可用

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

如果理解正确,需要基准测试的是clojure.lang.Util/hasheq针对Clojure类型的“分派时间”。因此,我反复对同一个持久哈希映射进行哈希,理论上是这个测试将只测量IHashEq实例的分派时间。然后我运行了一个单独的基准测试来哈希PHM,一个字符串和一个长整数,并使用unchecked-add将结果相加。希望这是一个好的开始;我无疑相信更多的基准测试会很有用。

结果让我有些意外:在我编译过程中,PHM上的hasheq比1.6.0略快;"添加三个hasheq"基准测试在1.6.0上略快。


;;; 1.6.0

;;; 注意:j.u.HM基准测试不相关
user=> (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)))
警告:最后的垃圾回收需要分配了1.24405836928592 %的运行时间
评估计数:60个样本中的9,249,268,385
             执行时间平均值:9.229881 ns
    执行时间标准偏差:0.156716 ns
   执行时间下四分位数:8.985994 ns( 2.5%)
   执行时间上四分位数:9.574039 ns(97.5%)
                  开销:1.741068 ns

在60个样本中发现了2个异常值(3.3333 %)
   低严重度     2 (3.3333 %)
 异常值方差:6.2652 % 异常值会导致方差略微膨胀
评估计数:60个样本中的5,942,128
             执行时间平均值:1.695145 µs
    执行时间标准偏差:20.186554 ns
   执行时间下四分位数:1.670049 µs( 2.5%)
   执行时间上四分位数:1.740329 µs(97.5%)
                  开销:1.741068 ns

在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)))))
警告:最终的GC需要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

;;;  patch applied

用户=> (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)))))
警告:最终的GC需要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更快,但将“三个哈希”基准测试的速度降低了大约2倍。
0

评论由:alexmiller 发布

为了明确,请根据名称引用此处附带的补丁,这样随着时间的推移我们就不必关联附件时间与评论时间。

我对实现IHashEq的东西的成本不太关心,因为它们除了可能的内联问题之外不应受到影响。我对具有hasheq的对象的成本很感兴趣,这些对象最终会进入cases的末尾并支付所有检查的成本。评论中列出的列表是一个很好的起点 —— 类似于Class、Character和Var(这可能会在Var中解决)。

0

评论人:michalmarczyk

很好,我已经修改了上面的评论,包括补丁名称。

感谢您提供的基准测试建议 - 我将在大约6分钟内发布一些新结果。

0

评论人:michalmarczyk

首先,为了完整性,这里有一个新的补丁(0001-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M-alternative.patch),它不会为默认情况下未处理的数据类型执行额外的murmur。对于单个PHM情况,见下文详情。此外,这里是在GitHub上的分支

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

至于新结果,性能影响相当大,我恐怕

`
;;; 使用补丁(murmur cornerstone for 默认版本)
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 ns ( 2.5%)
执行时间上四分位距 : 98.684168 ns (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 ns ( 2.5%)
执行时间上四分位距 : 90.755688 ns (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 ns ( 2.5%)
执行时间上四分位距 : 106.071144 ns (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 ns ( 2.5%)
执行时间上四分位距 : 34.990138 ns (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 ns ( 2.5%)
执行时间上四分位距 : 30.871497 ns (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 ns ( 2.5%)
执行时间上四分位距 : 36.918183 ns (97.5%)

               Overhead used : 1.718257 ns

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

low-severe	 1 (1.6667 %)

异常值方差 : 1.6389 % 异常值略微增加了方差
nil
`

使用新补丁(在默认情况下没有额外的murmur步骤)的One PHM和Class/Character/Var结果

`
用户=> (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 ns ( 2.5%)
执行时间上四分位距 : 59.154655 ns (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 ns ( 2.5%)
执行时间上四分位距 : 92.711120 ns (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 ns ( 2.5%)
执行时间上四分位距 : 85.569206 ns (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 ns ( 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),该方法采取了替换 Iterable/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)))
警告:最后的垃圾收集需要运行时的1.31690036780011%的时间
评估次数:1661453640次,60个样本,27690894次调用。

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

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

               Overhead used : 1.556642 ns

评估次数:1630167600次,60个样本,27169460次调用。

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

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

               Overhead used : 1.556642 ns

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

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

异常值方差 : 1.6389 % 异常值略微增加了方差
评估次数:1440434700次,60个样本,24007245次调用。

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

执行时间下四分位数:40.055991纳秒(2.5%)
执行时间上四分位数:41.990985纳秒(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 纳秒内)和“添加三个哈希值”基准的基准测试结果(带补丁为 66 纳秒,不带补丁为 57 纳秒)


用户=> (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)))
评估次数:5183841240次,60个样本,86397354次调用。
             执行时间平均值:10.076893纳秒
    执行时间标准差:0.182592纳秒
   执行时间下四分位数:9.838456纳秒(2.5%)
   执行时间上四分位数:10.481086纳秒(97.5%)
                     开销使用:1.565749纳秒
评估次数:3090420次,60个样本,51507次调用。
             执行时间平均值:19.596627微秒
    执行时间标准差:224.380257纳秒
   执行时间下四分位数:19.288347微秒(2.5%)
   执行时间上四分位数:20.085620微秒(97.5%)
                     开销使用:1.565749纳秒
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.418253438197936%的时间
评估次数:879210900次,60个样本,14653515次调用。
             执行时间平均值:66.939309纳秒
    执行时间标准差:0.747984纳秒
   执行时间下四分位数:65.667310纳秒(2.5%)
   执行时间上四分位数:68.155046纳秒(97.5%)
                     开销使用:1.724002纳秒
nil


需要注意的是,我在测试 1.6.0 时,在新的 JVM 上获得了不带补丁的“三个哈希值”基准测试结果,因此我也用该补丁应用了重复的基准测试。添加许多不同类型的类型会明显改变结果——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)))
警告:最终垃圾回收需要运行时的1.258979068087473%。
评估次数:1598432100,在60个样本中对26640535次调用进行了评估。

         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 % 异常值略微增加了方差
评估次数:1626362460,在60个样本中对27106041次调用进行了评估。

         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 % 异常值略微增加了方差
评估次数:1461423180,在60个样本中对24357053次调用进行了评估。

         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)))))
警告:最终垃圾回收需要运行时的1.5536755331464491%。
评估次数:820376460,在60个样本中对13672941次调用进行了评估。

         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)))
警告:最终垃圾回收需要运行时的1.260853406580491%。
评估次数:5369135760,在60个样本中对89485596次调用进行了评估。
             执行时间平均值: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%,方差严重受到异常值的影响
评估次数:3078180,在60个样本中对51303次调用进行了评估。
             执行时间平均值: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集合的hashcode使其与Clojure集合匹配的补丁,任何其他的更改都不在该工单的范围内。

总的来说,我目前更希望只报告执行时间的平均值,而不是一切——完整的criterium输出使得这些评论难以阅读和比较。

0

评论由:alexmiller 发布

能否提供一些方法的概述,以及对一组一致测试的1.6.0与每个补丁的对比时间的统计 - 比如说Long、PHM、juHM、Class和“三个hasheq”测试的哈希时间?

0
_由richhickey发表的评论_

“对许多不同类型的哈希会影响结果 - 可能是HotSpot在看到多个不同的类型传递给hasheq后,退出了某些优化?”

是的 - 如果你的基准测试没有将这个位置视为多态形状,你将会得到各种扭曲的结果。
0
_由:michalmarczyk发表评论

好的,我有一个我认为对此有所改进的微基准测试:long、double、字符串、类、字符和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}}。

3. {{Murmur3.hashOrdered}} 接收 {{Iterable}},这就是它为什么在列表上的原因。{{Map}} 不继承自 {{Iterable}},所以它单独列出。{{Map.Entry}} 在列表上,因为最终哈希地图的方式是遍历并哈希它们的条目。

4. 实际上对“异类”/宿主类型的哈希是由一个单独的静态方法完成的 -- {{clojure.lang.Util.doalienhasheq}} -- 假定这将使 {{hasheq}} 更容易内联,并限制最糟糕的性能下降到异类集合。

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

6. 对于其他 {{Iterable}} 类型也有一个默认情况 -- 我们必须返回它们的 {{hashCode}} 或将其他函数与 {{hashCode}} 组合的结果,因为我们使用 {{equals}} 来测试它们的等价性。

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

子串补丁只适用于 {{java.util.**}} 类,因此不能解决问题(例如,它不会正确哈希 Guava 集合)。

所有补丁都改变了 {{c.l.Util.hasheq}} 并向 {{clojure.lang.Util}} 添加了一个或两个新的静态方法,作为 {{hasheq}} 的辅助。它们都没有改变其他任何内容。Murmuring hashCode 是一个性能实验,似乎对一些“快速情况”有一些轻微的积极影响(事实上,它仍然是上述微基准测试中表现最好的三种补丁之一,胜利的差距当然是极其微小的)。因此我认为,所有当前的补丁实际上在范围上仅限于与条目直接相关的更改;-alternative 补丁和 0005 补丁确实如此。
...