请在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

截至2014年3月20日的最新Clojure master,两个补丁clj-1372.diff和clj-1372-2.diff都无法无忧地应用。我相信它们在2014年3月19日的提交之前是可以无忧地应用的,唯一的问题是diff上下文中改变了一行。鉴于关于是否希望进行此类更改的讨论,看起来在决定是否进行更改之前还需要更多的思考。

0 投票

评论人:mikera

这是一个相当严重的缺陷。它绝对必须修复。这不仅仅是一个问题,即使用Clojure和Java的集合混合是否是一个可能的用例(可能不是...),而是关于提供人人都可以依赖的保证。

例如,现在我对一些使用集合或映射的库函数是否损坏感到非常不确定。我特别担心任何基于哈希值实现的缓存、memoization或intern的代码。这样的代码现在可能存在一些非常微妙和严重的缺陷。

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

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.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 %的运行时间
评估次数:5549560980次,60次样本,每次样本调用92492683次。
执行时间平均值:9.229881 ns
执行时间标准差:0.156716 ns
执行时间下四分位数:8.985994 ns(2.5%)
执行时间上四分位数:9.574039 ns(97.5%)
开销用量:1.741068 ns

在60个样本中发现2个异常值。
                  低严重                   2(3.3333 %)
从异常值中变异的方差:6.2652 %,方差稍微因异常值而膨胀
评估次数:35647680次,60次样本,每次样本调用594128次。
               平均执行时间:1.695145 µs
    执行时间标准差:20.186554 ns
   执行时间下四分位数:1.670049 µs(2.5%)
   执行时间上四分位数:1.740329 µs(97.5%)
开销用量:1.741068 ns

在60个样本中发现2个异常值。
                  低严重                   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 ns
    执行时间标准差:0.732221 ns
   执行时间下四分位数:55.856731 ns(2.5%)
   执行时间上四分位数:58.469940 ns(97.5%)
                  开销:1.836671 ns

在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 ns
    执行时间标准差:0.157079 ns
   执行时间下四分位数:8.733544 ns(2.5%)
   执行时间上四分位数:9.289350 ns(97.5%)
                  开销:1.744772 ns
评估次数:2481600次,60个样本中的41360次调用。
             平均执行时间:24.287800 µs
    执行时间标准差:288.124326 ns
   执行时间下四分位数:23.856445 µs(2.5%)
   执行时间上四分位数:24.774097 µs(97.5%)
                  开销:1.744772 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.298136122909759 %的运行时间
评估次数:954751500次,60个样本中的15912525次调用。
             平均执行时间:61.681794 ns
    执行时间标准差:0.712110 ns
   执行时间下四分位数:60.622003 ns(2.5%)
   执行时间上四分位数:62.904801 ns(97.5%)
                  开销:1.744772 ns

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


作为一个旁注,另一个分支上可用的较早版本的补丁没有单独为String分支。这使得实现IHashEq的对象的hasheq更快,但将“三个哈希”基准测试的速度降低了大约2倍。
0 投票
by

由:alexmiller做出的评论

为了清晰起见,请按名称引用此处附上的补丁,以便随着时间的推移,我们不需要将附件时间与评论时间相关联。

我对实现IHashEq的功能的成本不是很担心,因为这些实现应该不会受到影响,除非存在潜在的内联问题。我对哈希eq对最终情况的物体和支付所有检查的成本很感兴趣。上面的注释列表是一个很好的开始地方——比如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

至于新的结果,性能损失相当大,我害怕

`
;; with patch (murmur hashCode for default version)
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)))
WARNING: Final GC required 1.409118084170768 % of runtime
Evaluation count : 655363680 in 60 samples of 10922728 calls.

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

Execution time lower quantile : 95.079086 ns ( 2.5%)
Execution time upper quantile : 98.684168 ns (97.5%)

               Overhead used : 1.708347 ns

Evaluation count : 675919140 in 60 samples of 11265319 calls.

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

Execution time lower quantile : 87.817159 ns ( 2.5%)
Execution time upper quantile : 90.755688 ns (97.5%)

               Overhead used : 1.708347 ns

Evaluation count : 574987680 in 60 samples of 9583128 calls.

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

Execution time lower quantile : 102.257474 ns ( 2.5%)
Execution time upper quantile : 106.071144 ns (97.5%)

               Overhead used : 1.708347 ns

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

low-severe	 1 (1.6667 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
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)))
WARNING: Final GC required 1.3353133083866688 % of runtime
Evaluation count : 1829305260 in 60 samples of 30488421 calls.

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

Execution time lower quantile : 33.680636 ns ( 2.5%)
Execution time upper quantile : 34.990138 ns (97.5%)

               Overhead used : 1.718257 ns

在60个样本中发现2个异常值。

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

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
Evaluation count : 1858100340 in 60 samples of 30968339 calls.

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

Execution time lower quantile : 30.095976 ns ( 2.5%)
Execution time upper quantile : 30.871497 ns (97.5%)

               Overhead used : 1.718257 ns

Evaluation count : 1592932200 in 60 samples of 26548870 calls.

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

Execution time lower quantile : 35.795063 ns ( 2.5%)
Execution time upper quantile : 36.918183 ns (97.5%)

               Overhead used : 1.718257 ns

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

low-severe	 1 (1.6667 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
nil
`

使用新补丁(默认情况下没有额外默念步骤)的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)))))
WARNING: Final GC required 1.258952964663877 % of runtime
Evaluation count : 1007768460 in 60 samples of 16796141 calls.

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

Execution time lower quantile : 57.655857 ns ( 2.5%)
Execution time upper quantile : 59.154655 ns (97.5%)

               Overhead used : 1.567532 ns

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

low-severe	 1 (1.6667 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
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)))
Evaluation count : 647944080 in 60 samples of 10799068 calls.

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

Execution time lower quantile : 90.330980 ns ( 2.5%)
Execution time upper quantile : 92.711120 ns (97.5%)

               Overhead used : 1.567532 ns

Evaluation count : 699506160 in 60 samples of 11658436 calls.

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

Execution time lower quantile : 83.765607 ns ( 2.5%)
Execution time upper quantile : 85.569206 ns (97.5%)

               Overhead used : 1.567532 ns

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

low-severe	 1 (1.6667 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
评估次数:594919980,来源于9915333次调用中的60个样本。

         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 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
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,来源于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 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
评估次数: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)会为java.util.**类(不包括List、Map、Map.Entry和Set)返回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


需要注意的是,我已在全新的JVM上进行“三个hasheq”基准测试时获得了无补丁结果,所以我也是这样重复基准测试的。对许多不同类型进行哈希会明显改变结果——也许HotSpot在看到将几种不同类型传递给hasheq后回退了一些优化?
0 投票

评论由:michalmarczyk

这是一个新的补丁(0005-CLJ-1372-consistent-hasheq-for-java.util.-List-Map-M.patch),它引入了一个新的isAlien静态方法,用于检查instanceof 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 %的运行时间
评估次数: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 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
评估次数: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 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
评估次数: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个异常值。

low-severe	 2 (3.3333 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
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 %的运行时间
评估次数: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个异常值。

low-severe	 2 (3.3333 %)

Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
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次,分布在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个异常值。
                  低严重                   2(3.3333 %)
 异常值方差:1.6389 % 异常值导致方差略微增加
nil
0 投票

由:alexmiller做出的评论

请不要提交任何更改哈希值的补丁,除非是为了让 Java 集合与 Clojure 集合匹配 - 任何其他更改都不在本次条目的范围之内。

总的来说,我目前更希望只看到执行时间的平均值报告,而不是全部内容 - 全部结果使这些评论难以阅读和比较。

0 投票

由:alexmiller做出的评论

能否给出各种方法的总结,以及对于一组一致测试的 1.6.0 版本与每个补丁的计时 - 比如 Long、PHM、juHM、Class 和 "三个 hasheq" 测试的时间?

0 投票
评论者:richhickey

“对不同类型的哈希编码进行编码会明显改变结果 - 大概是 HotSpot 在看到传递给 hasheq 的几个不同类型后放弃了一些优化?”

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

好的,我觉得这是一个改进的微观基准测试:一个长整型、一个 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。

Murmur3.hashOrdered接受Iterable,因此它在列表上。Map不扩展Iterable,因此它被单独列出。Map.Entry在列表上,因为最终散列地图的方式是遍历并散列它们的条目。

3. 实际散列“外星”/本地类型的散列是由一个单独的静态方法完成的--clojure.lang.Util.doalienhasheq--理论上是允许hasheq更激进地内联并最大限度地限制对“外星”集合性能的最坏影响。

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

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

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

子字符串补丁仅适用于java.util.**类,因此不足以解决问题(例如,它不能正确散列Guava集合)。

所有补丁都更改c.l.Util.hasheq并在clojure.lang.Util中添加一个或两个新的静态方法,作为hasheq的辅助。它们都没有改变其他任何内容。Murmuring hashCode是一个性能实验,似乎对某些“快速情况”有所改善(实际上,它仍然是当前上面提供的微基准测试中三个补丁中的最佳执行者,尽管胜利的差距当然非常小)。因此,我认为所有当前的补丁实际上在范围上仅限于与票据直接相关的更改;替代补丁和0005补丁确实如此。
...