XxHash: 非常快速的非加密哈希算法
( 如需查看英文版本,请 点击这里 )
xxHash-极快的哈希算法
xxHash是一种非常快速的哈希算法,在RAM速度限制下运行。它成功地完成了SMHasher测试套件,用于评估哈希函数的碰撞、分散和随机性。代码具有很高的可移植性,哈希在所有平台上都是相同的(little/big-endian)。
Benchmarks
参考系统使用Inteli7-9700Kcpu,运行UbuntuX64 20.04。开源基准测试程序是用clangv10.0使用-O3标志编译的。
Hash Name | Width | Bandwidth (GB/s) | Small Data Velocity | Quality | Comment |
XXH3 (SSE2) | 64 | 31.5 GB/s | 133.1 | 10 | |
XXH128 (SSE2) | 128 | 29.6 GB/s | 118.1 | 10 | |
RAM顺序读取 | N/A | 28.0 GB/s | N/A | N/A | for reference |
City64 | 64 | 22.0 GB/s | 76.6 | 10 | |
T1ha2 | 64 | 22.0 GB/s | 99.0 | 9 | 稍差的碰撞 |
City128 | 128 | 21.7 GB/s | 57.7 | 10 | |
XXH64 | 64 | 19.4 GB/s | 71.0 | 10 | |
SpookyHash | 64 | 19.3 GB/s | 53.2 | 10 | |
Mum | 64 | 18.0 GB/s | 67.0 | 9 | 稍差的碰撞 |
XXH32 | 32 | 9.7 GB/s | 71.9 | 10 | |
City32 | 32 | 9.1 GB/s | 66.0 | 10 | |
Murmur3 | 32 | 3.9 GB/s | 56.1 | 10 | |
SipHash | 64 | 3.0 GB/s | 43.2 | 10 | |
FNV64 | 64 | 1.2 GB/s | 62.7 | 5 | 雪崩特性差 |
Blake2 | 256 | 1.1 GB/s | 5.1 | 10 | Cryptographic |
SHA1 | 160 | 0.8 GB/s | 5.6 | 10 | 加密但已损坏 |
MD5 | 128 | 0.6 GB/s | 7.8 | 10 | 加密但已损坏 |
注1:小数据速率是对小数据的算法效率的粗略评估。有关更详细的分析,请参阅下一段。
注2:有些算法比RAM速度快。在这种情况下,只有当输入数据已经在CPU缓存(L3或更高)中时,它们才能达到全速。否则,它们会在RAM速度限制下达到最大值。
- Small data
对大数据的性能只是图片的一部分。哈希在哈希表和bloom过滤器等构造中也非常有用。在这些用例中,经常散列大量的小数据(从几个字节开始)。在这种情况下,算法的性能可能会有很大的不同,因为算法的某些部分,例如初始化或终结,变成了固定成本。分支mis-prediction的影响也变得更加明显。
XXH3的设计在长输入和小输入方面都具有出色的性能,可以在下图中观察到这一点:
有关更详细的分析,请访问wiki:https://github.com/Cyan4973/xxHash/wiki/Performance-comparison#benchmarks-concentrating-on-small-data-
Quality
速度不是唯一重要的属性。产生的散列值必须尊重优秀的分散性和随机性,这样它的任何sub-section都可以用来最大限度地扩展一个表或索引,并将碰撞量减少到最小理论水平,遵循生日悖论。
xxHash已经用austinappleby的优秀SMHasher测试套件进行了测试,并通过了所有测试,确保了合理的质量水平。它还通过了来自新的SMHasher分支的扩展测试,具有附加的场景和条件。
最后,xxHash提供了自己的大规模碰撞测试程序,能够生成和比较数十亿个哈希来测试64-bit哈希算法的极限。在这方面,xxHash也有很好的效果,符合生日悖论。wiki中记录了更详细的分析。
- Build modifiers
可以在编译时设置以下宏来修改libxxhash的行为。它们通常在默认情况下被禁用。
- XXH_INLINE_ALL:使所有函数inline,实现直接包含在xxhash.h中。内联函数有助于提高小按键的速度。当密钥长度被表示为编译时常数时,它非常有效,性能改进在+200%范围内。有关详细信息,请参阅本文。
- XXH_PRIVATE_API:与XXH_INLINE_ALL的结果相同。仍然可用于遗留支持。该名称强调XXH_*符号将不会导出。
- XXH_NAMESPACE:在所有符号前面加上XXH_NAMESPACE的值。此宏只能使用可编译字符集。当xxHash的源代码包含多个时,有助于避免符号命名冲突。客户机应用程序仍然使用常规函数名,因为符号是通过xxhash.h自动转换的。
- XXH_FORCE_MEMORY_ACCESS:默认方法0使用可移植的memcpy()符号。方法1使用gcc-specificpacked属性,该属性可以为某些目标提供更好的性能。方法2强制执行未对齐的读取,这不符合标准,但有时可能是提取更好的读取性能的唯一方法。方法3使用byteshift操作,这最适用于那些不在没有byteswap指令的memcpy()或big-endian系统的旧编译器
- XXH_FORCE_ALIGN_CHECK:对齐输入时使用更快的直接读取路径。当哈希的输入在32或64-bit边界上对齐时,当在无法从未对齐的地址加载内存的体系结构上运行时,或由于它而受到性能损失时,此选项可以显著提高性能。在具有良好的未对齐内存访问性能(对齐和未对齐访问的相同指令)的平台上,这是(稍微)有害的。此选项在x86、x64和aarch64上自动禁用,并在所有其他平台上启用。
- XXH_VECTOR:手动选择一个向量指令集(在编译时默认为auto-selected)。可用的指令集有XXH_SCALAR、XXH_SSE2、XXH_AVX2、XXH_AVX512、XXH_NEON和XXH_VSX。编译器可能需要额外的标志来确保正确的支持(例如,linux上的gcc对于AVX2需要-mavx2,对于AVX512需要-mavx512f)。
- XXH_NO_PREFETCH:禁用预取。仅限XXH3。
- XXH_PREFETCH_DIST:选择完善距离。仅限XXH3。
- XXH_NO_INLINE_HINTS:默认情况下,xxHash使用__attribute__((always_inline))和__forceinline来提高性能,但以代码大小为代价。将此宏定义为1将所有内部函数标记为static,允许编译器决定是否内联函数。这在优化最小二进制大小时非常有用,并且在GCC和Clang上使用-O0、-Os、-Oz或-fno-inline编译时自动定义。这还可以根据编译器和体系结构提高性能。
- XXH_REROLL:通过不展开某些循环来减少生成代码的大小。对性能的影响可能会有所不同,这取决于平台和算法。
- XXH_ACCEPT_NULL_INPUT_POINTER:如果设置为1,当输入是NULL指针时,xxHash的结果与zero-length输入相同(而不是取消引用segfault)。在每个哈希的开头添加一个分支。
- XXH_STATIC_LINKING_ONLY:为静态分配提供对状态声明的访问。由于ABI更改的风险,与动态链接不兼容。
- XXH_NO_LONG_LONG:删除依赖64-bit类型(XXH3和XXH64)的算法编译。只编译XXH32。对于没有64-bit支持的目标(体系结构和编译器)很有用。
- XXH_IMPORT:MSVC-specific:只应为动态链接定义,因为它可以防止链接错误。
- XXH_CPU_LITTLE_ENDIAN:默认情况下,endianess由编译时解析的运行时测试确定。如果由于某种原因,编译器不能简化运行时测试,则会降低性能。可以跳过auto-detection,通过将这个宏设置为1,简单地声明体系结构是little-endian。将其设置为0状态big-endian。
对于命令行界面xxhsum,还可以设置以下环境变量:
- DISPATCH=1:根据本地主机,使用xxh_x86dispatch.c在运行时自动选择scalar、sse2、avx2或avx512指令集。此选项仅对x86/x64系统有效。
- 正在构建xxHash-使用vcpkg
您可以使用vcpkg依赖关系管理器下载并安装xxHash:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install xxhash
vcpkg中的xxHash端口由Microsoft团队成员和社区贡献者保持最新。如果版本已过期,请在vcpkg存储库上创建问题或请求请求。
- Example
最简单的示例调用XXHASH 64-bit变体作为one-shot函数,从单个缓冲器生成哈希值,并从C/C++程序调用:
#include "xxhash.h"
(...)
XXH64_hash_t hash = XXH64(buffer, size, seed);
}
流式变体涉及更多,但可以增量提供数据:
#include "stdlib.h" /* abort() */
#include "xxhash.h"
XXH64_hash_t calcul_hash_streaming(FileHandler fh)
{
/* create a hash state */
XXH64_state_t* const state = XXH64_createState();
if (state==NULL) abort();
size_t const bufferSize = SOME_SIZE;
void* const buffer = malloc(bufferSize);
if (buffer==NULL) abort();
/* Initialize state with selected seed */
XXH64_hash_t const seed = 0; /* or any other value */
if (XXH64_reset(state, seed) == XXH_ERROR) abort();
/* Feed the state with input data, any size, any number of times */
(...)
while ( /* some data left */ ) {
size_t const length = get_more_data(buffer, bufferSize, fh);
if (XXH64_update(state, buffer, length) == XXH_ERROR) abort();
(...)
}
(...)
/* Produce the final hash value */
XXH64_hash_t const hash = XXH64_digest(state);
/* State could be re-used; but in this example, it is simply freed */
free(buffer);
XXH64_freeState(state);
return hash;
}
- License
库文件xxhash.c和xxhash.h是BSD授权的。实用程序xxhsum是GPL授权的。
- 其他编程语言
除了C参考版本之外,xxHash还可以从许多不同的编程语言中获得,这要感谢伟大的贡献者。它们列在这里。
- Special Thanks
Takayuki Matsuoka,又名@t-mat,用于在xxh早期版本中创建xxhsum -c和一般支持
Mathias Westerdahl,又名@JCash,因为他介绍了XXH64的第一个版本
Devin Hussey,又名@easyaspi314,他对XXH3和XXH128进行了出色的low-level优化