stdlib_hash_32bit
和 stdlib_hash_64bit
模块比较词法实体或其他对象的相等性在计算上可能代价很高。这种成本通常通过使用称为哈希函数的过程,从对象的结构计算一个接近唯一的整数值(称为哈希码)来降低。哈希码的相等是原始对象相等的必要条件,但不是充分条件。由于整数比较非常高效,因此先执行哈希码的初始比较,然后仅在哈希码相等时执行详细比较可以提高性能。哈希码又可以映射到一组较小的整数,这些整数可以用作索引(称为哈希索引)到秩 1 数组(通常称为哈希表)。这种映射将被称为标量哈希。使用哈希表减少了需要比较的哈希码数量,从而进一步提高了性能。哈希函数也可用于生成校验和,以验证数据是否已更改。因此,Fortran 标准库提供了用于计算哈希码和标量哈希的过程。本文档仅讨论库中的哈希码和标量哈希。
Fortran 标准库是在 MIT 许可证下分发的。但是,库的某些组件可能基于在不同许可证下发布的代码。特别是,哈希码通常基于被认为是公共领域的算法(Fibonacci Hash
、Universal Multiplicative Hash)
或在与 MIT 许可证不同的许可证下发布的算法(FNV-1 Hash
、FNV-1A Hash
、nmhash32
、nmhash32x
、waterhash
、pengyhash
和 SpookyHash
)算法的许可状态将在下面讨论。
fibonacci_hash
是一个标量哈希。它是 Fortran 2008 和带符号的二进制补码整数中对 D. E. Knuth 描述的斐波那契哈希的实现,“计算机程序设计艺术,第二版,第 3 卷,排序和搜索”,Addison-Wesley,新泽西州上萨德尔河,第 517-518 页,1998 年。该来源中的算法被认为是公共领域的,其使用不受限制。
universal_mult_hash
是一个标量哈希。它是 Fortran 2008 和带符号的二进制补码整数中对 M. Dietzfelbinger、T. Hagerup、J. Katajainen 和 M. Penttonen 的通用乘法哈希算法的实现,“一种可靠的随机化最近对问题算法”,J. 算法,第 25 卷,第 1 号,1997 年 10 月,第 19-51 页。由于其在算法杂志上发表,通用乘法哈希算法是公共领域的。
fnv_1_hash
和 fnv_1a_hash
是对 Glenn Fowler、Landon Curt Noll 和 Phong Vo 的 FNV-1
和 FNV-1a
哈希函数的 Fortran 2008 和带符号的二进制补码整数的翻译,这些函数已发布到公共领域。Landon Curt Noll 已授权在 Fortran 标准库中使用这些算法。这些函数的描述可在 https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function 中找到。这些函数已从其正常形式修改,以在输出哈希中也编码结构大小。
类似地,spooky_hash
和相关过程是对 Bob Jenkins 的无符号 64 位版本 2 SpookyHash
函数的 Fortran 2008 和带符号的二进制补码整数的翻译 https://burtleburtle.net/bob/hash/spooky.html 到带符号的 64 位运算。版本 2 被选择而不是版本 1,因为它具有更好的性能和更少的坏种子 Bob Jenkins 也将此代码置于公共领域,并已获得许可在美国将此代码视为公共领域,前提是该代码可以在其他许可证下使用,并且他得到了适当的认可。
nmhash32
和 nmhash32x
是对 James Z. M. Gao 的无符号 32 位哈希的 Fortran 2008 和带符号的二进制补码整数的翻译,nmhash32
和 nmhash32x
版本为 0.2,https://github.com/gzm55/hash-garage/blob/a8913138bdb3b7539c202edee30a7f0794bbd835/nmhash.h James Z. M. Gao 已在其 BSD 2 条款许可证下发布了他的代码。BSD 2 条款许可证如下
BSD 2-Clause License
Copyright (c) 2021, James Z.M. Gao
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
water_hash
是对 Tommy Ettinger 的 waterhash
算法的 Fortran 2008 和带符号的二进制补码整数的翻译。此算法受王毅的 Wy Hash 启发。Tommy Ettinger 的原始 C++ 代码,waterhash.h
,可在 URL:https://github.com/tommyettinger/waterhash 下获得,在 unlicense
下,https://github.com/tommyettinger/waterhash/blob/master/LICENSE。unlicense
的内容如下
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
pengy_hash
是对 Alberto Fajardo 版权 2020 年的 pengyhash
算法的 Fortran 2008 和带符号的二进制补码算术的翻译。Alberto Fajardo 的原始 C 代码,pengyhash.c
,可在 URL:https://github.com/tinypeng/pengyhash/blob/master/pengyhash.c 下获得,在 BSD 2 条款许可证下:https://github.com/tinypeng/pengyhash/blob/master/LICENSE
BSD 2 条款许可证如下
BSD 2-Clause License
pengyhash
Copyright (c) 2020 Alberto Fajardo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
本文档中使用了一些读者可能不熟悉的词语
键 - 用于在哈希表中查找条目,通常使用其哈希值进行初始搜索;
盐 - 请参阅种子,以及;
种子 - 哈希函数的附加参数,它会更改其输出,从而使某些攻击变得不切实际。
标准库提供了两个模块来实现哈希函数和标量哈希。stdlib_hash_32bit
模块提供了用于计算 32 位整数哈希码和标量哈希的过程。哈希码可用于最多 2**30
个条目的表,以及具有数百个元素的键,但性能仅针对最多 2**16
个条目的表进行了测试,并且对于更多条目的性能可能会下降。stdlib_hash_64bit
模块提供了用于计算 64 位整数哈希码和标量哈希的哈希过程。原则上,哈希码可用于最多 2**62
个条目的表,以及具有数千个元素的键,但性能测试仅针对最多 2**16
个元素的表进行,并且对于更多条目的性能可能会下降。虽然 stdlib_hash_64bit
中的一个代码 SPOOKY_HASH
也可用于计算 128 位哈希码,但当前没有任何代码可用于计算 256 位哈希码。这种更大的哈希码对于更大的哈希表和键以及校验和很有用。在当前的 Fortran 代码中,很少使用(如果使用的话)如此大的键和表,但如果需要,可以将更大的哈希码添加到库中。
哈希函数通常分为两类:“加密”和“非加密”。加密哈希函数生成的代码在没有生成代码所用哈希函数的身份之外的其他信息的情况下,难以逆转。在某些情况下,人们认为非加密代码是可逆的。这些模块仅实现被认为是非加密的哈希函数,并且其实现可在公共领域获得。
有许多可用于计算非加密 32 位和 64 位哈希码的算法,它们在计算复杂度、对不同大小键的相对性能以及预期结果哈希码的唯一性(随机性)方面有所不同。它们在文本分析中的相对性能,特别是可以取决于编译器、字符集、语言和内容。哈希函数的质量通常使用 SMHasher 测试套件进行评估,该套件最初由 Austin Appleby 编写,但由 Reini Urban 大大扩展。除了模块中定义的最简单的 FNV_1
和 FNV_1A
之外,所有哈希函数在 Reini Urban 版的 SMHasher 中的测试中都表现良好。
在 Fortran 中实现哈希函数存在两个问题。首先,Fortran 的静态类型使得定义通用哈希函数变得笨拙。相反,哈希函数是为一些更常见的对象定义的:字符字符串和秩为 1 的整数数组。原则上,其他对象可以通过使用 transfer
将其内容映射到整数数组(通常是 int8
类型)来进行哈希。另一个问题是哈希码通常使用模运算的无符号整数算术来定义。由于此类整数不是当前 Fortran 标准的一部分,因此必须使用变通方法。这些方法可以采取两种形式。一种是使用更大尺寸的整数来模拟运算,或者对于较大的整数,将其分成两个低位和高位部分,并使用较大的整数分别对每个部分进行运算。第二种方法是将无符号整数直接替换为相应的带符号整数,但在其他方面不修改代码逻辑。第一种方法应该在当前编译器上符合标准,但除非编译器识别很少在 Fortran 代码中使用的底层习语,否则计算量更大。第二种方法不符合标准,因为涉及符号的位运算未定义,但在具有二进制补码整数且不会在溢出或下溢时触发异常的编译器上,应该产生等效的结果。当前代码使用第二种方法。
为了编译哈希函数模块,编译器必须实现大部分 Fortran 2003 和 Fortran 2008 的选定组件:子模块、64 位整数和一些位内联函数。对有效编译器的主要限制是它们是否实现了 Fortran 2008 的子模块增强功能。为了正确运行哈希函数,编译器必须使用二进制补码整数,并且能够以循环语义执行它们,并且没有整数溢出异常。当前的 Fortran 2003+ 编译器仅使用二进制补码整数,并且似乎能够关闭溢出检测,因此模块使用带符号整数算术。因此,必须禁用带符号算术的异常捕获。下面表格总结了禁用实现子模块的编译器的溢出检测的命令行标志。请注意,FLANG、gfortran(从版本 10 开始)、ifort 和 NAG 都默认使用整数溢出循环。
编译器 | 合法标志 | 非法标志 | 默认值 |
---|---|---|---|
ARM Fortran | NA? | NA? | 溢出循环? |
Cray Fortran | NA? | NA? | 溢出循环? |
FLANG/PGI | -fwrapv | -ftrapv | -fwrapv |
gfortran | -fwrapv | -ftrapv | -fwrapv |
IBM Fortran | NA? | NA? | 溢出循环? |
ifort | NA? | NA? | 溢出循环 |
NAG Fortran | -C=none | -C=intovf | -C=none |
NEC Fortran | NA? | NA? | 溢出循环? |
NVIDIA Fortran | NA? | NA? | 溢出循环? |
所有模块的哈希函数都接受一个或两个参数。它们都将要哈希的对象作为第一个参数,称为键。大多数哈希函数都有第二个参数,称为种子,它设置哈希码的初始值,从而改变哈希函数的行为。特别是,使用给定种子哈希到相同哈希索引的输入,通常会在使用不同种子时哈希到不同的索引。这种行为差异使得使用种子的算法更能抵抗拒绝服务攻击,这些攻击利用已知哈希的特性来增加哈希表冲突的数量。此附加整数必须在给定哈希表中的所有哈希中保持相同,但如果冲突异常频繁,则可以更改并重新哈希对象。种子可以是标量或一个包含两个元素的数组。一些哈希函数有允许增量哈希的替代方案。
算法 | 种子 | 结果 |
---|---|---|
FNV-1 | 无 | 32 位或 64 位整数 |
FNV-1a | 无 | 32 位或 64 位整数 |
nmhash32 | 32 位标量整数 | 32 位整数 |
nmhash32x | 32 位标量整数 | 32 位整数 |
pengyhash | 32 位标量整数 | 64 位整数 |
Spooky Hash | 64 位包含两个元素的向量 | 64 位包含两个元素的向量 |
waterhash | 64 位标量整数 | 32 位整数 |
哈希函数模块每个都提供了至少五种哈希函数算法:两种针对小型(< 32 个 int8
整数元素)键进行了优化,三种针对大型(> 100 个 int8
整数元素)键进行了优化。每种算法的核心实现都是针对作为 int8
整数向量类型的键。然后,这些核心实现用于作为 int16
、int32
和 int64
整数向量或默认字符字符串类型的键的包装器,期望内联将消除将其他键转换为 int8
整数向量带来的开销。
stdlib_hash_32bit
模块提供了五种哈希码算法的实现:Glenn Fowler、Landon Curt Noll 和 Kiem-Phong Vo 的 FNV_1 和 FNV_1A 变体;James Z. M. Gao 的 nmhash32 和 nmhash32x;以及 Tommy Ettinger 的 waterhash。每种算法的详细实现都由一个单独的子模块处理:stdlib_hash_32bit_fnv
、stdlib_hash_32bit_nm
和 stdlib_hash_32bit_water
。nmhash32
、nmhash32x
和 waterhash
算法需要种子。子模块为每种算法提供单独的种子生成器。模块本身实现了两个标量哈希函数,fibonacci_hash
和 universal_mult_hash
。它还实现了子程序 odd_random_integer
,用于为 universal_mult_hash
生成种子。所有这些都假设二进制补码符号位,并且没有超出范围检查。
stdlib_hash_64bit
模块还提供了四种哈希码算法的实现:Glenn Fowler、Landon Curt Noll 和 Kiem-Phong Vo 的 FNV_1 和 FNV_1A 变体;Alberto Fajardo 的 pengyhash;以及 Bob Jenkins 的 SpookyHash。每种算法的详细实现都由一个单独的子模块处理:stdlib_hash_64bit_fnv
、stdlib_hash_64bit_pengy
和 stdlib_hash_64bit_spooky
。pengyhash
和 Spooky Hash
算法需要种子。子模块为每种算法提供单独的种子生成器。模块本身实现了两个标量哈希函数,fibonacci_hash
和 universal_mult_hash
。它还实现了子程序 odd_random_integer
,用于为 universal_mult_hash
生成种子。所有这些都假设二进制补码符号位,并且没有超出范围检查。
stdlib_hash_32bit_fnv
和 stdlib_hash_64bit_fnv
子模块分别以两个单独的重载函数的形式提供了 FNV-1 和 FNV-1A 算法的实现:FNV_1
和 FNV_1A
。FNV-1 和 FNV-2 算法在乘法和异或运算的顺序上有所不同。它们与它们的正常实现不同之处在于,它们还将结构大小编码到哈希码中。32 位和 64 位算法在其初始偏移量和乘法常数上有所不同。分析表明,FNV_1A
应该更擅长随机化输入,但哈希表测试表明差异可以忽略不计。这些算法以特别适用于小字节字符串(即长度小于 32 字节的字符串)而闻名。虽然它们在 SMHasher 测试套件上的性能并不理想,但使用情况表明,这对小型哈希表的性能影响很小,并且函数的小尺寸允许它们快速加载并保留在指令缓存中,从而在哈希间歇性进行时提高性能。(请参阅SMHasher 讨论和S. Richter、V. Alvarez 和 J. Dittrich。2015。哈希方法的七维分析及其对查询处理的影响,VLDB 捐赠论文集,第 9 卷,第 3 期。https://doi.org/10.14778/2850583.2850585。)
stdlib_hash_32bit_nm
子模块以重载函数 nmhash32
和 nmhash32x
的形式提供了 James Z.M. Gao 的 nmhash32
和 nmhash32x
算法(版本 0.2)的实现。这些实现基于 Gao 算法的标量版本,而不是需要访问某些编译器的向量指令的向量版本。这两种算法在 SMHasher 测试中表现良好,并且没有已知的坏种子。两种代码的向量版本在大型键上表现良好,其中 nmhash32x
在短键上更快。为了为这两个函数提供随机生成的种子,子模块还定义了子程序 new_nmhash32_seed
和 new_nmhash32x_seed
。Gao 声称 nmhash32x
在短种子上的速度明显快于 nmhash32
,但在长种子上的速度较慢,但我们迄今为止的有限测试表明 nmhash32x
在短种子上的速度明显更快,在长种子上的速度略快。
stdlib_hash_32bit_water
子模块以重载函数 water_hash
的形式提供了 Tommy Ettinger 的 waterhash
算法的实现。Water Hash 尚未经过 Reini Urban 测试,但 Tommy Ettinger 已使用 Urban 的 SMHasher 对其进行了测试,并提供了结果,表明 Water Hash 通过了所有测试。到目前为止,他的测试尚未发现该算法有任何坏种子。为了为哈希函数提供随机生成的种子,子模块还定义了子程序 new_water_hash_seed
。
stdlib_hash_64bit_pengy
子模块以重载函数 pengy_hash
的形式提供了 Alberto Fajardo 的 pengyhash
的实现。Reini Urban 的测试表明,PengyHash 通过了所有测试并且没有坏种子。为了为哈希函数提供随机生成的种子,子模块还定义了子程序 new_pengy_hash_seed
。
stdlib_hash_64bit_spooky
子模块以重载函数 spooky_hash
的形式提供了 Bob Jenkins 的 SpookyHash 的实现。未来的实现可能会提供 SpookyHash 增量哈希过程。SpookyHash 针对大型对象进行了优化,对于大于约 96 字节的对象应该提供出色的性能,但对于较小的对象则有明显的开销。该代码专为小端编译器设计,在大端编译器上会产生不同的结果,但这些编译器上的哈希质量可能也一样好。SpookyHash 版本 2 通过了 Reini Urban 的所有 SMHasher 测试,并且仅在减少到 32 位输出时才有一个坏种子。它唯一潜在的问题是在键未对齐时出现未定义行为。
stdlib_hash_32bit
模块32 位哈希函数主要用于为哈希表生成哈希码和哈希索引。它们往往不太适用于生成校验和,校验和通常受益于具有更多位的特性。stdlib_hash_32bit
模块定义了五个公共重载的 32 位哈希码函数,FNV_1
、FNV-1A
、nmhash32
、nmhash32x
和 water_hash
,两个标量哈希函数,fibonacci_hash
和 universal_mult_hash
,四个种子生成器,odd_random_integer
用于 universal_mult_hash
,以及 new_nmhash32_seed
、new_nmhash32x_seed
和 new_water_hash_seed
,分别用于其各自的哈希码函数。它还定义了整数种类常量 int_hash
和逻辑常量 little_endian
,用于处理哈希码机器依赖性的一个方面。
int_hash
参数有必要定义用于返回哈希码的整数种类。由于 stdlib_hash_32bit
专门处理 32 位哈希码,因此 int_hash
是整数种类 int32
的别名。
little_endian
参数在实现哈希函数时,有时需要知道编译器整数的“字节序”。为此,stdlib_hash_32bit
模块定义了逻辑参数 little_endian
,如果为真,则表示编译器具有小端整数,如果为假,则表示整数是大端整数。
stdlib_hash_32bit
过程的规范fibonacci_hash
- 将整数映射到较少的位数实验性的
从 32 位整数计算一个 nbits
哈希码。这在将哈希码映射到小型数组中很有用。
code =
fibonacci_hash ( key, nbits )
基本函数
key
: 应为 int32
类型的标量整数表达式。它是 intent(in)
参数。
nbits
应为标量默认整数表达式,且满足 0 < nbits < 32
。它是 intent(in)
参数。
结果为 int32
类型的整数,最多只有最低 nbits
位非零,映射到 0 到 nbits-1
的范围。
fibonacci_hash
是 Donald E. Knuth 提出的斐波那契哈希的实现。它将 key
乘以 2**32/phi
的奇数值近似值,其中 phi
是黄金分割率 1.618...,并将乘积的 nbits
个高位作为结果的低位返回。
program example_fibonacci_hash
use stdlib_hash_32bit, only: fibonacci_hash
use iso_fortran_env, only: int32
implicit none
integer, allocatable :: array1(:)
integer(int32) :: hash, source
allocate (array1(0:2**6 - 1))
array1(:) = 0
source = 42_int32
hash = fibonacci_hash(source, 6)
array1(hash) = source
print *, hash
end program example_fibonacci_hash
fnv_1_hash
- 从键计算哈希码实验性的
从秩为 1 的整数数组或默认字符字符串计算 32 位哈希码。
code =
fnv_1_hash ( key )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
fnv_1_hash
是 Glenn Fowler、Landon Curt Noll 和 Phong Vo 提出的原始 FNV-1 哈希码的实现。它与典型实现的不同之处在于,它还在哈希码中编码了结构的大小。此代码在短键上相对较快,并且足够小,以至于如果哈希是间歇性的,它通常会保留在指令缓存中。因此,它应该为典型的哈希表应用程序提供良好的性能。此代码未通过任何 SMHasher 测试,但由于其较多的冲突而导致的性能下降预计将比其更快的哈希速度小得多。对于整数数组,它是一个纯函数,对于字符字符串,它是一个基本函数。
program example_fnv_1_hash
use stdlib_hash_32bit, only: fnv_1_hash
use iso_fortran_env, only: int32
implicit none
integer(int32) :: hash
hash = fnv_1_hash([5, 4, 3, 1, 10, 4, 9])
print *, hash
end program example_fnv_1_hash
fnv_1a_hash
- 从键计算哈希码实验性的
从秩为 1 的整数数组或默认字符字符串计算 32 位哈希码。
code =
fnv_1a_hash ( key )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
fnv_1a_hash
是 Glenn Fowler、Landon Curt Noll 和 Phong Vo 提出的备选 FNV-1a 哈希码的实现。它与典型实现的不同之处在于,它还在哈希码中编码了结构的大小。此代码在短键上相对较快,并且足够小,以至于如果哈希是间歇性的,它通常会保留在指令缓存中。因此,它应该为典型的哈希表应用程序提供良好的性能。此代码未通过任何 SMHasher 测试,但由于其较多的冲突而导致的性能下降预计将比其更快的哈希速度小得多。对于整数数组,它是一个纯函数,对于字符字符串,它是一个基本函数。
program example_fnv_1a_hash
use stdlib_hash_32bit, only: fnv_1a_hash
use iso_fortran_env, only: int32
implicit none
integer(int32) :: hash
hash = fnv_1a_hash([5, 4, 3, 1, 10, 4, 9])
print *, hash
end program example_fnv_1a_hash
new_nmhash32_seed
- 返回 nmhash32
的有效输入种子实验性的
计算一个 32 位“随机”整数,据信它是 nmhash32
的有效种子,并且也与输入种子不同。
call
new_nmhash32_seed ( seed )
子程序
seed
: 应为 int32
类型的已定义整数标量变量。它是 intent(inout)
参数。输入时 seed
应已定义,输出时它将与输入 seed
不同。
目前 nmhash32
没有已知的坏种子,但如果发现任何坏种子,则将修改此过程,以便无法返回它们。此子程序使用 Fortran 的内建函数 random_number
,并且返回的值可以通过调用内建函数 random_init
来更改。
请参阅 nmhash32
的示例。
new_nmhash32x_seed
- 返回 nmhash32x
的有效输入种子实验性的
计算一个 32 位“随机”整数,据信它是 nmhash32x
的有效种子,并且也与输入种子不同。
call
new_nmhash32x_seed ( seed )
子程序
seed
: 应为 int32
类型的已定义整数标量变量。它是 intent(inout)
参数。输入时 seed
应已定义,输出时它将与输入 seed
不同。
目前 nmhash32x
没有已知的坏种子,但如果发现任何坏种子,则将修改此过程,以便无法返回它们。此子程序使用 Fortran 的内建函数 random_number
,并且返回的值可以通过调用内建函数 random_init
来更改。
请参阅 nmhash32x
的示例。
new_water_hash_seed
- 返回 water_hash
的有效输入种子实验性的
计算一个 64 位“随机”整数,据信它是 water_hash
的有效种子,并且也与输入种子不同。
call
new_water_hash_seed ( seed )
子程序
seed
: 应为 int64
类型的已定义整数标量变量。它是 intent(inout)
参数。输入时 seed
应已定义,输出时它将与输入 seed
不同。
目前 water_hash
没有已知的坏种子,但如果发现任何坏种子,则将修改此过程,以便无法返回它们。此子程序使用 Fortran 的内建函数 random_number
,并且返回的值可以通过调用内建函数 random_init
来更改。
请参阅 water_hash
的示例。
nmhash32
- 从键和种子计算哈希码实验性的
从秩为 1 的整数数组或默认字符字符串以及输入 seed
计算 32 位哈希码。
code =
nmhash32 ( key, seed )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
seed
: 应为 int32
类型的整数标量表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
nmhash32
是 James Z. M. Gao 提出的 nmhash32
哈希码的实现。此代码在长键上具有良好但并非最佳的性能,在短键上性能较差。因此,它应该为典型的哈希表应用程序提供公平的性能。此代码通过了 SMHasher 测试,并且没有已知的坏种子。对于整数数组,它是一个纯函数,对于字符字符串,它是一个基本函数。
program example_nmhash32
use stdlib_hash_32bit, only: nmhash32, &
new_nmhash32_seed
use iso_fortran_env, only: int32
implicit none
integer(int32) :: hash
integer(int32) :: seed = 42_int32
call new_nmhash32_seed(seed)
hash = nmhash32([5, 4, 3, 1, 10, 4, 9], seed)
print *, seed, hash
end program example_nmhash32
nmhash32x
- 从键和种子计算哈希码实验性的
从秩为 1 的整数数组或默认字符字符串以及输入 seed
计算 32 位哈希码。
code =
nmhash32x ( key, seed )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
seed
: 应为 int32
类型的整数标量表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
nmhash32x
是 James Z. M. Gao 提出的 nmhash32x
哈希码的实现。此代码在长键上具有良好但并非最佳的性能,在短键上性能较差。因此,它应该为典型的哈希表应用程序提供公平的性能。此代码通过了 SMHasher 测试,并且没有已知的坏种子。对于整数数组,它是一个纯函数,对于字符字符串,它是一个基本函数。
program example_nmhash32x
use stdlib_hash_32bit, only: nmhash32x, &
new_nmhash32x_seed
use iso_fortran_env, only: int32
implicit none
integer(int32) :: hash
integer(int32) :: seed = 42_int32
call new_nmhash32x_seed(seed)
hash = nmhash32x([5, 4, 3, 1, 10, 4, 9], seed)
print *, seed, hash
end program example_nmhash32x
odd_random_integer
- 返回奇数整数实验性的
返回一个在奇数值上均匀分布的随机 32 位整数。
call
odd_random_integer ( harvest )
子程序
harvest
: 应为 int32
类型的标量整数变量。它是 intent(out)
参数。
odd_random_integer
用于为 universal_mult_hash
生成种子。odd_random_integer
使用 Fortran 的内建函数 random_number
,并且返回的值可以通过调用内建函数 random_init
来更改。
请参阅 universal_mult_hash
。
universal_mult_hash
- 将整数映射到较少的位数实验性的
从 32 位整数计算 nbits
哈希码。这在将哈希值映射到 0 到 2**nbits-1
的范围时很有用。
code =
universal_mult_hash ( key, seed, nbits )
基本函数
key
: 应为 int32
类型的标量整数表达式。它是 intent(in)
参数。
seed
: 应为 int32
类型的标量整数表达式。它是 intent(in)
参数。它必须具有奇数值。
nbits
应为标量默认整数表达式,且满足 0 < nbits < 32
。它是 intent(in)
参数。
结果为 int32
类型的标量整数,最多只有最低 nbits
位非零。
universal_mult_hash
是 M. Dietzfelbinger 等人提出的通用乘法哈希的实现。它将 key
乘以 seed
,并将乘积的 nbits
个高位作为结果的低位返回。
program example_universal_mult_hash
use stdlib_hash_32bit, only: odd_random_integer, &
universal_mult_hash
use iso_fortran_env, only: int32
implicit none
integer, allocatable :: array1(:)
integer(int32) :: hash, i, seed, source
seed = 0
allocate (array1(0:2**6 - 1))
do i = 0, 2**6 - 1
array1(i) = i
end do
call odd_random_integer(seed)
source = 42_int32
hash = universal_mult_hash(source, seed, 6)
array1(hash) = source
print *, seed, hash, array1
end program example_universal_mult_hash
water_hash
- 从键和种子计算哈希码实验性的
从秩为 1 的整数数组或默认字符字符串以及输入 seed
计算 32 位哈希码。
code =
water_hash ( key, seed )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
seed
: 应为 int64
类型的整数标量表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
water_hash
是 Tommy Ettinger 提出的 waterhash
哈希码的实现。此代码在长键上具有出色的性能,在短键上具有良好的性能。因此,它应该为典型的哈希表应用程序提供合理的性能。此代码通过了 SMHasher 测试。waterhash
基于 Wang Yi 的 wyhash
。虽然 wyhash
有许多坏种子,其中输出的随机性较差,但到目前为止,测试尚未发现 waterhash
的任何坏种子。如果键未对齐,则它可能具有未定义的行为,即某些计算机处理器只能在整数地址是整数大小的倍数时处理给定大小的整数。对于整数数组,它是一个纯函数,对于字符字符串,它是一个基本函数。
program example_water_hash
use stdlib_hash_32bit, only: water_hash, &
new_water_hash_seed
use iso_fortran_env, only: int32, int64
implicit none
integer(int32) :: hash
integer(int64) :: seed = 42_int64
call new_water_hash_seed(seed)
hash = water_hash([5, 4, 3, 1, 10, 4, 9], seed)
print *, hash, seed
end program example_water_hash
stdlib_hash_64bit
模块对于哈希表应用程序,64 位哈希函数通常是过度杀伤,主要用于校验和相关应用程序。由于校验和通常必须处理非常大的文件或目录,因此使用增量哈希和直接哈希通常很有用,因此 64 位及更高位的哈希算法通常提供多种实现。出于 API 简化目的,当前模块不提供任何增量哈希。stdlib_hash_64bit
模块定义了几个公共重载的 64 位哈希过程,FNV_1
、FNV-1A
、pengy_hash
和 spooky_hash
,两个标量哈希函数,fibonacci_hash
和 universal_mult_hash
,一个种子生成器,odd_random_integer
,用于 universal_mult_hash
,以及两个种子生成器,new_pengy_hash_seed
和 new_spooky_hash_seed
,分别用于其各自的哈希函数。它还定义了整数类型常量 int_hash
,用于指定哈希函数结果的类型,以及逻辑常量 little_endian
,用于处理哈希码的机器依赖性的一个方面。请注意,虽然 SpookyHash 可以用作 64 位哈希算法,但其算法实际上返回 int64
类型的两个元素整数数组,因此它也可以用作 128 位哈希。
int_hash
参数有必要定义用于返回哈希码的整数类型。由于 stdlib_haash_64bit
专门处理 64 位哈希码,因此 int_hash
是整数类型 int64
的别名。
little_endian
参数在实现哈希函数时,有时需要知道编译器整数的“字节序”。为此,stdlib_hash_64bit
模块定义了逻辑参数 little_endian
,如果为真,则表示编译器具有小端整数,如果为假,则表示整数是大端整数。
stdlib_hash_64bit
过程的规范fibonacci_hash
- 将整数映射到较少的位数实验性的
从 64 位整数计算 nbits
哈希码。这在将哈希码映射到小型数组中时很有用。
code =
fibonacci_hash ( key, nbits )
基本函数
key
: 应为 int64
类型的标量整数表达式。它是 intent(in)
参数。
nbits
应为标量默认整数表达式,且满足 0 < nbits < 64
。它是 intent(in)
参数。
结果是一个 int64
类型的整数,最多只有最低 nbits
位非零,映射到 0 到 nbits-1
的范围。
fibonacci_hash
是 Donald E. Knuth 提出的斐波那契哈希的实现。它将 key
乘以 2**64/phi
的奇数近似值,其中 phi
是黄金分割率 1.618...,并返回乘积的 nbits
个高位作为结果的低位。
program example_fibonacci_hash_64
use stdlib_hash_64bit, only: fibonacci_hash
use iso_fortran_env, only: int64
implicit none
integer, allocatable :: array1(:)
integer(int64) :: hash, source
allocate (array1(0:2**6 - 1))
array1(:) = 0
source = int(Z'1FFFFFFFF', int64)
hash = fibonacci_hash(source, 6)
array1(hash) = source
print *, hash
end program example_fibonacci_hash_64
FNV_1
- 根据键计算哈希码实验性的
根据秩为 1 的整数数组或默认字符字符串计算 64 位哈希码。
code =
fnv_1_hash ( key )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
结果是一个 int64
类型的标量整数。
FNV_1
是 Glenn Fowler、Landon Curt Noll 和 Phong Vo 提出的原始 FNV-1 哈希码的实现。它与典型的实现不同,因为它还在哈希码中编码了结构的大小。此代码在短键上相对较快,并且足够小,以至于如果散列是间歇性的,它通常会保留在指令缓存中。因此,它应该为典型的哈希表应用程序提供良好的性能,尽管它们很少需要 64 位。此代码未通过任何 SMHasher 测试,但由于其碰撞次数更多而导致的性能下降预计会比其更快的散列速度小得多。对于整数数组,它是一个纯函数,对于字符字符串,它是一个元素函数。
program example_fnv_1_hash_64
use stdlib_hash_64bit, only: fnv_1_hash
use iso_fortran_env, only: int64
implicit none
integer, allocatable :: array1(:)
integer(int64) :: hash
array1 = [5, 4, 3, 1, 10, 4, 9]
hash = fnv_1_hash(array1)
print *, hash
end program example_fnv_1_hash_64
FNV_1A
- 根据键计算哈希码实验性的
根据秩为 1 的整数数组或默认字符字符串计算 64 位哈希码。
code =
fnv_1a_hash ( key )
纯/基本函数
key
: 应为延迟长度默认字符标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数数组表达式。它是 intent(in)
参数。
结果为 int32
类型的标量整数。
FNV_1A
是 Glenn Fowler、Landon Curt Noll 和 Phong Vo 提出的备选 FNV-1a 哈希码的实现。它与典型的实现不同,因为它还在哈希码中编码了结构的大小。此代码在短键上相对较快,并且足够小,以至于如果散列是间歇性的,它通常会保留在指令缓存中。因此,它应该为典型的哈希表应用程序提供良好的性能。此代码未通过任何 SMHasher 测试,但由于其碰撞次数更多而导致的性能下降预计会比其更快的散列速度小得多。对于整数数组,它是一个纯函数,对于字符字符串,它是一个元素函数。
program example_fnv_1a_hash_64
use stdlib_hash_64bit, only: fnv_1a_hash
use iso_fortran_env, only: int64
implicit none
integer, allocatable :: array1(:)
integer(int64) :: hash
array1 = [5, 4, 3, 1, 10, 4, 9]
hash = fnv_1a_hash(array1)
print *, hash
end program example_fnv_1a_hash_64
new_pengy_hash_seed
- 返回 pengy_hash
的有效输入种子实验性的
计算一个 32 位“随机”整数,该整数被认为是 pengy_hash
的有效种子,并且也与输入种子不同。
call
new_pengy_hash_seed ( seed )
子程序
seed
: 应为 int32
类型的已定义整数标量变量。它是 intent(inout)
参数。输入时 seed
应已定义,输出时它将与输入 seed
不同。
目前,pengy_hash
没有已知的坏种子,但如果发现任何坏种子,则将修改此过程,以便它们不会被返回。此子程序使用 Fortran 的内在函数 random_number
,并且返回的值可以通过调用内在函数 random_init
来更改。
请参阅 pengy_hash
的示例。
new_spooky_hash_seed
- 返回 spooky_hash
的有效输入种子实验性的
计算一个 32 位的两个元素向量,该向量包含“随机”整数值,被认为是 spooky_hash
的有效种子,并且也与输入种子不同。
call
new_spooky_hash_seed ( seed )
子程序
seed
:应为 int32
类型的已定义的两个元素整数向量变量。它是一个 intent(inout)
参数。在输入时,seed
应已定义,在输出时,它将与输入 seed
不同。
目前,spooky_hash
没有已知的坏种子,但如果发现任何坏种子,则将修改此过程,以便它们不会被返回。此子程序使用 Fortran 的内在函数 random_number
,并且返回的值可以通过调用内在函数 random_init
来更改。
请参阅 spooky_hash
的示例。
odd_random_integer
- 返回奇数整数实验性的
返回一个随机的 64 位整数,该整数均匀分布在奇数值上。
call
odd_random_integer ( harvest )
子程序
harvest
:应为 int64
类型的整数。它是一个 intent(out)
参数。
odd_random_integer
用于为 universal_mult_hash
生成种子。odd_random_integer
使用 Fortran 的内建函数 random_number
,并且返回的值可以通过调用内建函数 random_init
来更改。
请参阅 universal_mult_hash
。
pengy_hash
- 将字符字符串或整数向量映射到整数实验性的
将字符字符串或整数向量映射到一个 64 位整数,其值还取决于一个 32 位标量整数 seed
。
code =
pengy_hash ( key, seed )
纯/基本函数
key
:应为默认字符类型的标量表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数向量表达式。它是一个 intent(in)
参数。
seed
:应为 int64
类型的整数表达式。它是一个 intent(in)
参数。
结果是一个 int64
类型的整数。
pengy_hash
是 Alberto Fajardo 提出的 64 位 pengyhash
的实现。该哈希在小键上具有可接受的性能,在大键上具有良好的性能。它通过了所有 SMHasher 测试,并且没有已知的坏种子。对于整数数组,它是一个纯函数,对于字符字符串,它是一个元素函数。
program example_pengy_hash
use stdlib_hash_64bit, only: new_pengy_hash_seed, pengy_hash
use iso_fortran_env, only: int32, int64
implicit none
integer, allocatable :: key(:)
integer(int64) :: hash
integer(int32) :: seed
key = [0, 1, 2, 3]
seed = 0_int32
call new_pengy_hash_seed(seed)
hash = pengy_hash(key, seed)
print *, seed, hash
end program example_pengy_hash
spooky_hash
- 将字符字符串或整数向量映射到整数实验性的
将字符字符串或整数向量映射到一个 64 位整数,其值还取决于一个两个元素的向量 seed
。
code =
spooky_hash ( key, seed )
函数
key
:应为默认字符类型表达式或 int8
、int16
、int32
或 int64
类型的秩为 1 的整数向量表达式。它是一个 intent(in)
参数。
seed
:应为 int64
类型的两个元素整数向量表达式。它是一个 intent(in)
参数。
结果是一个 int64
类型的两个元素整数向量。
spooky_hash
是 Bob Jenkins 提出的 SpookyHash 64 位版本 2 的实现。该代码是为小端编译器设计的。在大端编译器上的输出不同,但质量可能仍然一样好。它通常用作使用返回值的第一个元素的 64 位哈希,但可以用作 128 位哈希。此版本的 spooky_hash
在小键上具有良好的性能,在大键上具有极佳的性能。它通过了所有 SMHasher 测试,并且没有已知的坏种子。
program example_spooky_hash
use stdlib_hash_64bit, only: new_spooky_hash_seed, &
spooky_hash
use iso_fortran_env, only: int64
implicit none
integer, allocatable :: key(:)
integer(int64) :: hash(2), seed(2)
key = [0, 1, 2, 3]
seed = [119_int64, 2_int64**41 - 1]
call new_spooky_hash_seed(seed)
hash = spooky_hash(key, seed)
print *, seed, hash
end program example_spooky_hash
universal_mult_hash
- 将整数映射到较少的位数实验性的
根据 64 位整数计算一个 nbits
哈希码。这在将哈希值映射到 0 到 2**nbits-1
的范围内很有用。
code =
universal_mult_hash ( key, seed, nbits )
基本函数
key
:应为 int64
类型的整数。它是一个 intent(in)
参数。
seed
:应为 int64
类型的整数。它是一个 intent(in)
参数。它应该是一个奇数。
nbits
应为默认整数,且 0 < nbits < 64
。它是一个 intent(in)
参数。它必须是一个奇数。
结果是一个 int64
类型的整数,最多只有最低 nbits
位非零。
universal_mult_hash
是 M. Dietzfelbinger 等人提出的通用乘法哈希的实现。它将 key
乘以 seed
,并将乘积的 nbits
个高位作为结果的低位返回。
program example_universal_mult_hash_64
use stdlib_hash_64bit, only: odd_random_integer, &
universal_mult_hash
use iso_fortran_env, only: int64
implicit none
integer, allocatable :: array1(:)
integer(int64) :: hash, seed, source
seed = 0
allocate (array1(0:2**6 - 1))
array1 = 0
call odd_random_integer(seed)
source = 42_int64
hash = universal_mult_hash(source, seed, 6)
array1(hash) = source
print *, seed, hash, array1
end program example_universal_mult_hash_64
Fortran 标准库提供了两类测试代码。一类是各种哈希函数的相对性能测试。另一类是比较 Fortran 哈希函数的输出与作为 Fortran 哈希函数灵感的 C 和 C++ 哈希过程的输出。
在 test/hash_functions_perf
子目录中,Fortran 标准库为 stdlib_hash_32bit
和 stdlib_hash_64bit
的哈希函数提供了两个性能测试代码,分别为 test_32_bit_hash_performance
和 test_64_bit_hash_performance
。这些主要用于测试函数的运行时性能。它们获取 2**18
个 int8
类型的整数样本,并将其分解为大小为 1、2、4、8、16、64、256 和 1024 个元素的向量,分别产生 2**18
、2**17
、2**16
、2**15
、2**14
、2**12
、2**10
和 2**8
个向量。然后,这些向量由哈希函数处理 4 次,并报告处理时间。到目前为止,测试是在配备 2.3 GHz 四核英特尔酷睿 i5 和 8 GB 2133 MHz LPDDR3 RAM 的 MacBook Pro 上进行的,使用 GNU Fortran (GCC) 11.1.0 编译代码。test_32_bit_hash_performance
的结果由下表给出
算法 | 键大小(字节) | 键数 | 时间(秒) |
---|---|---|---|
FNV-1 | 1 | 1048576 | 0.02949 |
FNV-1 | 2 | 524288 | 0.02361 |
FNV-1 | 4 | 262144 | 0.02016 |
FNV-1 | 8 | 131072 | 0.01806 |
FNV-1 | 16 | 65536 | 0.01867 |
FNV-1 | 64 | 16384 | 0.01717 |
FNV-1 | 256 | 4096 | 0.01759 |
FNV-1 | 1024 | 1024 | 0.01659 |
FNV-1a | 1 | 1048576 | 0.02897 |
FNV-1a | 2 | 524288 | 0.02472 |
FNV-1a | 4 | 262144 | 0.02025 |
FNV-1a | 8 | 131072 | 0.01901 |
FNV-1a | 16 | 65536 | 0.01898 |
FNV-1a | 64 | 16384 | 0.01784 |
FNV-1a | 256 | 4096 | 0.01723 |
FNV-1a | 1024 | 1024 | 0.01673 |
nmhash32 | 1 | 1048576 | 0.31092 |
nmhash32 | 2 | 524288 | 0.16230 |
nmhash32 | 4 | 262144 | 0.07815 |
nmhash32 | 8 | 131072 | 0.04176 |
nmhash32 | 16 | 65536 | 0.09261 |
nmhash32 | 64 | 16384 | 0.04587 |
nmhash32 | 256 | 4096 | 0.07238 |
nmhash32 | 1024 | 1024 | 0.07263 |
nmhash32x | 1 | 1048576 | 0.04294 |
nmhash32x | 2 | 524288 | 0.02937 |
nmhash32x | 4 | 262144 | 0.01096 |
nmhash32x | 8 | 131072 | 0.00911 |
nmhash32x | 16 | 65536 | 0.01291 |
nmhash32x | 64 | 16384 | 0.00859 |
nmhash32x | 256 | 4096 | 0.07373 |
nmhash32x | 1024 | 1024 | 0.07618 |
water | 1 | 1048576 | 0.12560 |
water | 2 | 524288 | 0.06302 |
water | 4 | 262144 | 0.04020 |
water | 8 | 131072 | 0.01999 |
water | 16 | 65536 | 0.01459 |
water | 64 | 16384 | 0.00923 |
water | 256 | 4096 | 0.00816 |
water | 1024 | 1024 | 0.00792 |
而对于 test_64_bit_hash_performance
,结果为
算法 | 键大小(字节) | 键数 | 时间(秒) |
---|---|---|---|
FNV-1 | 1 | 1048576 | 0.02981 |
FNV-1 | 2 | 524288 | 0.02697 |
FNV-1 | 4 | 262144 | 0.02275 |
FNV-1 | 8 | 131072 | 0.02431 |
FNV-1 | 16 | 65536 | 0.02158 |
FNV-1 | 64 | 16384 | 0.02007 |
FNV-1 | 256 | 4096 | 0.01932 |
FNV-1 | 1024 | 1024 | 0.02089 |
FNV-1a | 1 | 1048576 | 0.03226 |
FNV-1a | 2 | 524288 | 0.03076 |
FNV-1a | 4 | 262144 | 0.02359 |
FNV-1a | 8 | 131072 | 0.02542 |
FNV-1a | 16 | 65536 | 0.02364 |
FNV-1a | 64 | 16384 | 0.02130 |
FNV-1a | 256 | 4096 | 0.01962 |
FNV-1a | 1024 | 1024 | 0.01966 |
Pengy | 1 | 1048576 | 0.24294 |
Pengy | 2 | 524288 | 0.12066 |
Pengy | 4 | 262144 | 0.06205 |
Pengy | 8 | 131072 | 0.03138 |
Pengy | 16 | 65536 | 0.01608 |
Pengy | 64 | 16384 | 0.00669 |
Pengy | 256 | 4096 | 0.00387 |
Pengy | 1024 | 1024 | 0.00295 |
Spooky | 1 | 1048576 | 0.11920 |
Spooky | 2 | 524288 | 0.07478 |
Spooky | 4 | 262144 | 0.03185 |
Spooky | 8 | 131072 | 0.01468 |
Spooky | 16 | 65536 | 0.01503 |
Spooky | 64 | 16384 | 0.00440 |
Spooky | 256 | 4096 | 0.00290 |
Spooky | 1024 | 1024 | 0.00177 |
由于被测函数通常会驻留在指令缓存中,因此这些结果不包括如果散列是间歇性的则重新加载过程的成本。如果散列是间歇性的,那么这可能会更严重地影响 nmhash32
、nmhash32x
、water_hash
、pengy_hash
和 spooky_hash
相对于 fnv_1_hash
和 fnv_1a_hash
的性能。
在 test/hash_functions
子目录中,Fortran 标准库包含代码以测试 Fortran 代码相对于原始 C 和 C++ 代码的有效性。它包含一个可执行文件 test_hash_functions
,它 1) 生成一个 2048 个 int8
类型整数的随机序列,并将该序列存储在二进制文件 key_array.bin
中;2) 读取 key_array.bin
中的值,并且对于每个复杂的 C/C++ 编码的哈希过程,生成一个相应的二进制文件,其中包含从 key_array.bin
中的值生成的 2049 个哈希值,以及 3) 读取二进制文件,并且对于每个复杂的 C/C++ 编码的哈希过程,将二进制文件的内容与使用相应的 Fortran 哈希过程对相同键计算哈希值的结果进行比较。