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 提出的评论:

clj-1372.diff 和 clj-1372-2.diff 两个补丁在 2014 年 3 月 20 日最新的 Clojure master 上无法干净地应用。我相信,在 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}的=操作一致。它还扩展了字符串的特殊处理(返回hashCode的hasheq),以处理所有未处理的其他类型(有关此问题的评论如下)。

它也在这里可用

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

早期版本在这里

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

如果理解正确,需要测试的主要是给定的Clojure类型对clojure.lang.Util/hasheq的"分派时间"。因此,我重复地计算持久哈希映射,以测量IHashEq实例的分派时间。然后运行另一个仅针对PHM、字符串和长整数的哈希 benchmark,并将结果相加的unchecked-add。希望这是一个好的开始;毫无疑问,额外的基准测试将是很有用的。

结果让我有些惊讶:在我的构建中,PHM的hasheq在这个benchmark上实际上比1.6.0要快;"添加三个hasheq"的benchmark在1.6.0上略快。


;;; 1.6.0

;;; 注意。j.u.HM benchmark不相关
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)))
WARNING: 最终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

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)))))
WARNING: 最终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创建单独的分支。这使hasheq对实现IHashEq的对象更快,但“三个散列”基准测试大约慢了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

至于新的结果,性能下降相当大,恐怕是这样的

`
;;;;
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和类/字符/变量结果

`
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),它采取了过分的方法,用类名上的.startsWith("java.util.")来替换Iterable/Map/Entry测试。(我试验过.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%的百分比
评估次数:1661453640次,分布在27690894次调用的60个样本中。

         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次,分布在27169460次调用的60个样本中。

         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次,分布在24007245次调用的60个样本中。

         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纳秒之内)和“添加三个hasheq”基准测试(带补丁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次,分布在86397354次调用的60个样本中。
             平均执行时间:10.076893纳秒
    执行时间标准差:0.182592纳秒
   执行时间下四分位数:9.838456纳秒(2.5%)
   执行时间上四分位数:10.481086纳秒(97.5%)
                   额外开销:1.565749纳秒
评估次数:3090420次,分布在51507次调用的60个样本中。
             平均执行时间:19.596627微秒
    执行时间标准差:224.380257纳秒
   执行时间下四分位数:19.288347微秒(2.5%)
   执行时间上四分位数:20.085620微秒(97.5%)
                   额外开销:1.565749纳秒
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次,分布在14653515次调用的60个样本中。
             平均执行时间:66.939309纳秒
    执行时间标准差:0.747984纳秒
   执行时间下四分位数:65.667310纳秒(2.5%)
   执行时间上四分位数:68.155046纳秒(97.5%)
                   额外开销:1.724002纳秒
nil


需要注意,我在对1.6.0基准测试时,已经获得了“三个hasheq”基准测试的无补丁结果,因此我用补丁重复了基准测试。不同类型的哈希改变结果明显——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%的运行时间
评估次数:每60个样本中1598432100次,共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 %,由于异常值,方差略有膨胀
评估次数:每60个样本中1626362460次,共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 %,由于异常值,方差略有膨胀
评估次数:每60个样本中1461423180次,共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”基准
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%的运行时间
评估次数:每60个样本中820376460次,共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实例的hasheq对基准——这里都有,为了完整性(没有惊喜)


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%的运行时间
评估次数:每60个样本中5369135760次,共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% 异常值严重增加了变异
评估次数:每60个样本中3078180次,共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集合以匹配Clojure集合以外的更改hashCode的补丁——任何其他更改都不在本次工单的范围之内。

一般来说,我此刻更喜欢只报告平均执行时间,而不是所有内容——完整的评价标准输出使这些评论更难阅读和比较。

0 投票

评论者:alexmiller

能否总结一下方法,并提供一组持续测试对应的1.6.0版本与每个修补程序的对比时间 - 举例来说,对于Long、PHM、juHM、Class以及“三个hasheqs”测试,请给出它们的哈希值?”

0 投票
_评论由:richhickey_提交

“对多种不同类型的哈希操作会明显改变结果 - 猜测是HotSpot在下看到几个不同类型的输入后放弃了一些优化?”

没错 - 如果你的基准测试没有将这个站点视为多态的,你将得到各种各样的扭曲结果。
0 投票
_评论由:michalmarczyk_发布

好的,我有一个我认为是改进的微观基准测试:对于Long、double、字符串、类、字符和PHM(单例,因此它将是哈希查找),hasheqs的异或操作。结果并不令人鼓舞。

包含{{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. 在默认返回值的哈希值上方添加一个额外的{{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 补丁调用一个单独的私有静态方法来执行 三方类型检查,而其他版本则是将检查直接放入实际的 {{if}} 表达式中。-alternative 补丁和 0005 补丁在默认情况下返回 {{hashCode}},而原始补丁则是将 {{Murmur3.hashInt}} 与 {{hashCode}} 结合。

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

所有补丁都修改了 {{c.l.Util.hasheq}},并在 {{clojure.lang.Util}} 中添加一个或两个新的静态方法作为 {{hasheq}} 的辅助,但没有修改其他任何内容。MurmuringhashCode 是一个性能实验,看来对一些“快速情况”有轻微的积极影响(实际上,它在上面提供的微基准测试中仍然是三个补丁中表现最好的,尽管胜利的幅度当然非常微小)。因此,我认为所有当前补丁实际上仅限于与票据直接相关的变化;-alternative 补丁和 0005 补丁显然是如此。
...