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集合的混合是否是一个可能的使用场景(可能不是...),而是关于提供人们可以依赖的一致保证。

例如,我现在真的很不确定我所使用的某些使用集合或映射的库函数是否已经损坏。我特别担心任何基于哈希值实现对象缓存/记忆化/内部化的代码。这样的代码现在可能存在一些非常隐蔽的严重缺陷。

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

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的“分派时间”。因此,我多次对相同的持久哈希映射进行散列,以测量IHashEq实例上的分派时间。然后我对PHM、字符串和长整型进行单独的基准测试,并将结果通过unchecked-add累加。希望这是一个好的开始;我相信还有很多其他基准测试将是有用的。

结果让我有些惊讶:PHM上的hasheq在这个基准测试中比我构建的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需要runtime的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个异常值(3.3333%)
    low-severe     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个异常值(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需要runtime的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的成本并不十分担心,因为它们应该不受影响,除了可能的内联问题。我对到达cases末尾支付所有检查成本的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 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步骤)的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 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 ns(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)))
警告:最终GC占用了1.31690036780011 %的运行时间
评估次数:60个样本中的27690894次调用,共1661453640次。

         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

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

         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 % 方差略因出类拔萃者而通胀
评估次数:60个样本中的24007245次调用,共1440434700次。

         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纳秒)。


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)))
评估次数:60个样本中的86397354次调用,共5183841240次。
             平均执行时间:10.076893纳秒
    执行时间标准差:0.182592纳秒
   执行时间下四分位数:9.838456纳秒(2.5%)
   执行时间上四分位数:10.481086纳秒(97.5%)
                   额外费用:1.565749纳秒
评估次数:60个样本中的51507次调用,共3090420次。
             平均执行时间: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 %的运行时间
评估次数:60个样本中的14653515次调用,共879210900次。
             平均执行时间:66.939309纳秒
    执行时间标准差:0.747984纳秒
   执行时间下四分位数:65.667310纳秒(2.5%)
   执行时间上四分位数:68.155046纳秒(97.5%)
                   额外费用:1.724002纳秒
nil


需要注意的是,在基准测试1.6.0时,我已经获得了一个未进行补丁的“三个哈希等”基准测试结果,因此我也这样重复了应用补丁后的基准测试。对许多不同类型进行哈希会明显改变结果——可能是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)))
警告:最终的垃圾回收占运行时时间的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

;;; "三个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)))))
警告:最终的垃圾回收占运行时时间的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实例对基准测试——这里为了完整性列出(但没有惊喜)


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.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%)
    low-severe     2 (3.3333 %)
 异常值方差:1.6389 %,方差略微由异常值膨胀
nil
0

评论者:alexmiller

请不要提交任何更改hashcode的补丁,除非是为了使Java集合与Clojure集合匹配——任何其他更改都超出此工单的范围。

总的来说,我此时更愿意只报告执行时间均值,而不是所有内容——完整的输出使得这些注释难以阅读和比较。

0

评论者:alexmiller

能否提供一系列方法的总览,以及1.6.0版与每个补丁针对一组测试的计时——例如Long、PHM、juHM、Class和“三个hashes”测试的哈希时间?

0
_由 richhickey 评论:

"对许多不同类型的哈希处理会导致结果显著变化——HotSpot可能看到向hasheq传递了几种不同类型后减慢了某些优化。"

是的——如果你的基准测试没有将此站点视为多态,你会得到各种扭曲的结果。
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. 在默认返回的{{hasheq}}上方添加了一个额外的{{if}}语句,用于三次{{instanceof}}检查。

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

{{Murmur3.hashOrdered}}接受{{Iterable}},因此它列在清单上。{{Map}}不扩展{{Iterable}},因此它单独列出。{{Map.Entry}}列在清单上,因为最终哈希地图的方式是迭代其条目并进行哈希处理。

4. “外星”/主机类型的实际哈希操作是通过一个单独的静态方法完成的 -- {{clojure.lang.Util.doalienhasheq}} -- 依据的原理是这可以使得 {{hasheq}} 能够更积极地进行内联,并限制最差的性能影响在“外星”集合上。

{{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 补丁肯定如此。
...