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

欢迎!请参阅关于页面以了解更多关于此工作方式的信息。

0
Collections

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

根据最新Clojure master的2014年3月20日的结果,补丁文件clj-1372.diff和clj-1372-2.diff在无法干净地应用时失败。我相信它们在2014年3月19日的提交之前是干净应用过的,唯一的问题似乎是一行diff上下文发生了变化。考虑到关于是否需要进行此类更改的讨论,这听起来在决定是否做出更改之前需要进一步的思考。

0

评论由:mikera

这是一个很严重的缺陷。绝对需要修复。这真正关于是否使用Clojure和Java集合的混合是可能的用例(可能并不是...),而是关于提供人们可以信赖的一致保证。

例如,现在我对所使用的集合或映射库函数是否损坏 Really 没有把握。 我会对基于哈希值实现对象缓存/记忆化/内联化的一切 特别关心。 这类代码现在可能存在一些非常令人讨厌的微妙缺陷。

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

0
_评论由: michalmarczyk_ 提出

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

它也在这里可用

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 累加结果。 希望这是一个好的开始;我相信额外的基准测试会有所帮助。

结果对我来说是有点意外的:hasheq 在 PHM 上的确实在这个基准测试中比我构建的 1.6.0 快一点;在 1.6.0 上,“添加三个 hasheq”基准测试稍微快一点。


;;; 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)))
警告:最终 GC 需要 1.24405836928592 % 的运行时间
评估数 : 5549560980 在 60 个样本中,每个样本包含 92492683 个调用。
             执行时间平均值 : 9.229881 纳秒
    执行时间标准差 : 0.156716 纳秒
   执行时间下限四分位数 : 8.985994 纳秒 ( 2.5%)
   执行时间上限四分位数 : 9.574039 纳秒 ( 97.5%)
               开销使用: 1.741068 纳秒

在 60 个样本中找到 2 个异常值(3.3333%)
    low-severe     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%)
    low-severe     2 (3.3333%)
 从异常值中得出的方差: 1.6389 % 方差稍微偏高,因为包含了异常值
nil

user=> (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 % 的运行时间
评估数: 1029948300 在 60 个样本中,每个样本包含 17165805 个调用。
             执行时间平均值: 56.797488 纳秒
    执行时间标准差:0.732221纳秒
   执行时间下四分位数:55.856731纳秒(2.5%)
   执行时间上四分位数:58.469940纳秒(97.5%)
             额外开销:1.836671纳秒

在60个样本中发现1个异常值(1.6667%)
   低严重程度     1(1.6667%)
 从异常值中得出的方差: 1.6389 % 方差稍微偏高,因为包含了异常值
nil

;;; 修订已应用

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)))
评估次数:5537698680次,共60个样本,调用次数为92294978次。
            平均执行时间:8.973200纳秒
    执行时间标准差:0.157079纳秒
   执行时间下四分位数:8.733544纳秒(2.5%)
   执行时间上四分位数:9.289350纳秒(97.5%)
        额外开销:1.744772纳秒
评估次数:2481600次,共60个样本,调用次数为41360次。
             平均执行时间:24.287800微秒
    执行时间标准差:288.124326纳秒
   执行时间下四分位数:23.856445微秒(2.5%)
   执行时间上四分位数:24.774097微秒(97.5%)
        额外开销:1.744772纳秒
nil

user=> (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 %
评估次数:954751500次,共60个样本,调用次数为15912525次。
             平均执行时间: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的事物的成本,因为它们应该不会受到影响,除了可能的内联问题。我对落到case末尾并承担所有检查成本的对象的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
`

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

`
user=> (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),它采取了用.class.getName().startsWith("java.util.")替换Iterable/Map/Entry测试的极端方法。(我尝试了.getClass().getPackage()},但那个性能太糟糕了。)分支在这里

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

这个补丁对于“fall-through”情况的哈希性能似乎非常好

`
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 %的运行时间
评估次数:1661453640,在60个样本中的27690894次调用。

         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

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

         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 % 异常值导致方差略微膨胀
评估次数:1440434700,在60个样本中的24007245次调用。

         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纳秒之内)和“添加三个hashes”的基准测试(带补丁66纳秒,不带补丁57纳秒)


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)))
评估次数:5183841240,在60个样本中的86397354次调用。
             平均执行时间:10.076893 ns
    执行时间标准偏差:0.182592 ns
   执行时间下限:9.838456 ns(2.5%)
   执行时间上限:10.481086 ns(97.5%)
                  使用的开销:1.565749 ns
评估次数:3090420,在60个样本中的51507次调用。
             平均执行时间:19.596627 µs
    执行时间标准偏差:224.380257 ns
   执行时间下限:19.288347 µs(2.5%)
   执行时间上限:20.085620 µs(97.5%)
                  使用的开销:1.565749 ns
nil

user=> (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 %的运行时间
评估次数:879210900,在60个样本中的14653515次调用。
             平均执行时间:66.939309 ns
    执行时间标准偏差:0.747984 ns
   执行时间下限:65.667310 ns(2.5%)
   执行时间上限:68.155046 ns(97.5%)
                  使用的开销:1.724002 ns
nil


请注意,我在基准测试1.6.0时,在一个新的JVM上获得了“三个hashes”的基准测试的无补丁结果,因此我就是这样应用补丁后重复基准测试的。不同类型的哈希会明显改变结果——可能是HotSpot在看到多个不同类型传入hasheq后从某些优化中退出了?
0

评论由:michalmarczyk发表

这是一个新补丁(0005-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M.patch),它引入了一个名为isAlien的新静态方法,该方法检查对于Map/Map.Entry/Iterable的instanceof,并使用此方法来测试“外星集合”。

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

`
“直通”基准测试
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 %的运行时间
评估次数: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 次在27106041次调用中的60个样本。

         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 次在24357053次调用中的60个样本。

         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

;;; “三个hashes”基准测试
user=> (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%
评估次数:820376460 次在13672941次调用中的60个样本。

         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实例对的基准测试,为了完整性下面放上来(没有惊喜)


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)))
警告:最后一次垃圾回收(GC)需要的运行时百分比:1.260853406580491%
评估次数:5369135760 次在89485596次调用中的60个样本。
            执行时间平均值: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 次在51303次调用中的60个样本。
            执行时间平均值:19.717981 微秒
    执行时间标准差:209.896848 纳秒
   执行时间下四分位数:19.401811 微秒 (2.5%)
   执行时间上四分位数:20.180163 微秒 (97.5%)
            使用的开销:1.566301 纳秒

在 60 个样本中找到 2 个异常值(3.3333%)
    low-severe     2 (3.3333%)
 从异常值中得出的方差: 1.6389 % 方差稍微偏高,因为包含了异常值
nil
0
作者:

评论由:alexmiller发表

请不要提交任何更改hashCode以外的补丁,除非是为了使Java集合与Clojure集合匹配 - 任何其他变更都不在此任务的范围之内。

一般来说,我此时更愿意看到只是执行时间平均值的报告,而不是全部内容 - 完整的criterium输出使这些评论的阅读和比较更加困难。

0
作者:

评论由:alexmiller发表

能否提供一个方法的摘要,以及1.6.0版本与每个补丁之间的时间对比,针对一组连续的测试 - 比如哈希代码的Long、PHM、juHM、Class和“三个哈希eq”测试的时间?

0
通过
评论者:richhickey

"对许多不同类型进行哈希会导致结果明显改变 - 着眼于HotSpot在看到传递给hasheq的几个不同类型后放弃了一些优化?”

是的 - 如果你的基准测试没有将这个站点视为多态的,你将会得到各种扭曲的结果。
0
通过
_评论由: michalmarczyk_ 提出

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

包括{{require}}的单个形式以方便运行;并且还捆绑了一个{{j.u.HashMap}}(128个条目)hasheq基准测试


(
  (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补丁确实如此。
...