可以使用 CRC32C 作为基础构造一个“好”的哈希函数吗?

Posted

技术标签:

【中文标题】可以使用 CRC32C 作为基础构造一个“好”的哈希函数吗?【英文标题】:Can one construct a "good" hash function using CRC32C as a base? 【发布时间】:2011-02-11 06:59:42 【问题描述】:

鉴于 SSE 4.2(英特尔酷睿 i7 和 i5 部件)包含 CRC32 指令,调查是否可以构建更快的通用哈希函数似乎是合理的。根据this,只有 16 位 CRC32 是均匀分布的。那么还有什么其他的转变可以用来克服这个问题呢?

更新 这个怎么样?只有 16 位适用于哈希值。美好的。如果您的表是 65535 或更少,那就太好了。如果不是,则通过 Nehalem POPCNT(人口计数)指令运行 C​​RC 值以获取设置的位数。然后,将其用作表数组的索引。如果您的桌子位于 1 毫米条目的南边,则此方法有效。我敢打赌,这比性能最好的哈希函数更便宜/更快。既然GCC 4.5 有一个 CRC32 内在函数,它应该很容易测试……如果我有足够的空闲时间来研究它。

大卫

【问题讨论】:

【参考方案1】:

重温,2014 年 8 月Arnaud Bouchez 在最近的评论中提示,并鉴于其他答案和 cmets,我承认需要更改原始答案或为最不合格的答案。最后我保留原样,以供参考。

首先,也许也是最重要的一点,这个问题的公平答案取决于哈希码的预期用途:“好”[哈希函数...] 是什么意思?哈希将在哪里/如何使用? (例如,它是用于散列相对较短的输入键吗?是用于索引/查找目的,生成消息摘要还是其他用途?所需的散列码本身有多长,所有 32 位 [CRC32 或其衍生物],更多位,更少……等等? OP 问题要求“更快 通用散列函数”,因此重点是速度(CPU 密集度较低和/或可以利用各种性质的并行处理的东西)。我们在这里可能会注意到,哈希码本身的计算时间通常只是哈希应用中问题的一部分(例如,如果哈希码的大小或其固有特性导致许多冲突,需要处理额外的周期和)。此外,“通用”的要求也留下了许多关于可能用途的问题。

考虑到这一点,一个简短而更好的答案可能是:

是的,CRC32C 在较新的英特尔处理器上的硬件实现可用于构建更快的哈希码;但是请注意,根据散列的具体实现及其应用,由于冲突的频率以及需要使用更长的代码,总体结果可能不是最佳的。此外,当然,应该仔细审查哈希的加密使用,因为 CRC32 算法本身在这方面非常薄弱。

原始答案引用了 Bret Mulvey 的一篇关于评估哈希函数的文章,正如 Mdlg 的答案中所指出的,这篇文章的结论对于 CRC32 是错误的,因为它是基于 CRC32 的实现有缺陷/有缺陷。尽管在 CRC32 方面存在重大错误,但本文提供了有关哈希算法一般属性的有用指导。这篇文章的 URL 现已失效;我在archive.today找到了它,但我不知道作者是否在其他地方有它,也不知道他是否更新了它。

此处的其他答案引用 CityHash 1.0 作为使用 CRC32C 的哈希库的示例。显然,这是在一些更长(超过 32 位)哈希码的上下文中使用的,但不适用于 CityHash32() 函数本身。此外,与生成哈希码的所有移位和混洗以及其他操作相比,City Hash 函数对 CRC32 的使用相对较少。 (这不是对我没有实践经验的 CityHash 的批评。我将通过对 CityHash 函数产生好的源代码的粗略审查,例如 ell 分布式代码,但没有明显更快比其他各种哈希函数。)

最后,您还可以在quasi duplicate question on SO 中找到有关此问题的见解。


原始答案和编辑(2010 年 4 月)

先验这听起来是个坏主意!

CRC32 不是为散列目的而设计的,它的分布可能不均匀,因此使其成为相对较差的散列码。此外,它的“加扰”能力相对较弱,导致单向散列非常差,就像在加密应用中使用的那样。

[BRB:我正在寻找有关该效果的在线参考资料...]

Google 的第一个 [keywords = CRC32 distribution] 似乎证实了这一点:Evaluating CRC32 for hash tables

编辑:上面引用的页面,实际上complete article 提供了在哈希函数中查找内容的良好基础。 阅读 [快速] 这篇文章,确认了 一般 CRC32 不应用作散列的一揽子声明,但是,根据散列的特定目的,可能可以使用,在至少部分是 CRC32 作为哈希码。

例如,CRC32 代码的较低(或较高,取决于实现)16 位具有相对均匀的分布,并且只要不关心哈希码的加密属性(例如,事实相似的密钥产生非常相似的代码),有可能构建一个哈希码,例如,将两个 CRC32 代码的低 [或高] 16 位连接起来,由两个部分(或任何除法)产生原始密钥。 需要运行测试以查看内置 CRC32 指令相对于替代散列函数的效率是否会导致两次调用指令并将代码拼接在一起等的开销不会导致整体较慢的功能。

【讨论】:

MJV,感谢您为提供民兵可能所说的“......对已经理解的内容的更丰富的理解”所做的努力。事后我为没有更明确表示歉意。 CRC 专为散列目的而设计,冲突的可能性最小。通用(与安全无关)散列使用很好。 @rsking。不完全是。最小化可能的冲突数量是 CRC 设计的次要目标;主要目标是最大化其错误检测性能在特定预期密钥分布的上下文中对于纯随机密钥,这两个目标是完全兼容的,但是,CRC通常是在特定通道中选择的请记住,无论是就其典型内容而言,还是就其最常见的错误模式而言。 CRC32 尤其如此,K Brayer 和 J Hammond 在 1975 年的论文中特别提到了这一点。此外... ... CRC32 分布不均匀这一事实可以通过各种经验测试来断言,例如答案中提到的测试。这种糟糕的[整体]分布不是设计缺陷,而是确认重点是限制冲突[“本地”]对于提交到相同噪声通道的类似长度的消息而不是任意消息服从随机噪声。因此,CRC 不一定非常适合用作通用哈希。 -1 引用的文章用作参考,使用了错误的 crc32 实现 - 请参阅下面的 Mdlg 答案。所以这篇文章不是“寻找哈希函数的好基础”。我希望看到这个答案更新。根据我自己的实验,crc32 是非常好的哈希函数候选者。【参考方案2】:

其他答案中提到的文章基于有缺陷的 crc32 代码得出了错误的结论。谷歌的排名算法尚未根据科学准确性进行排名。

与参考文章"Evaluating CRC32 for hash tables" 的结论相反,CRC32 和CRC32C 可用于哈希表。作者的示例代码在 crc32 表生成中存在 bug。修复 crc32 表,使用相同的方法得到令人满意的结果。此外,CRC32 指令的速度使其成为许多情况下的最佳选择。使用 CRC32 指令的代码在峰值时比最佳软件实现快 16 倍。 (注意CRC32与intel指令实现的CRC32C并不完全相同。)

CRC32 显然不适合加密使用。 (32位是蛮力的笑话)。

【讨论】:

+1 值得一提的是,引用的文章错误实现了crc32!在实践中,处理 UTF-8 文本时,我们发现 crc32 在速度和碰撞方面是最好的折衷方案(例如比 Kernighan & Ritchie、BobJenkins、FNV1a 更好)。最新的 SSE4.2 CPU 确实具有硬编码的 crc32c 指令,它在性能方面优于其他所有 CPU。见blog.synopse.info/post/2014/05/25/… 和delphitools.info/2014/08/25/string-hashing-shootout/… 不适用于加密:不仅可以轻松暴力破解,还可以解析求解。【参考方案3】:

是的。CityHash 1.0.1 包含一些使用 CRC32 指令的新“良好哈希函数”。

【讨论】:

【参考方案4】:

只要您不使用加密哈希,它就可以工作。

【讨论】:

【参考方案5】:

出于加密目的,CRC32 是一个糟糕的基础,因为它是线性的(在向量空间 GF(2)^32 上)并且很难纠正。它可以用于非加密目的。

但是,最新的英特尔内核具有AES-NI 指令,基本上在两个时钟周期内执行 AES 块加密的 1/10。它们在最新的 i5 和 i7 处理器上可用(有关详细信息,请参阅Wikipedia page)。 看起来是构建加密哈希函数的良好开端(对加密有益的哈希函数也适用于其他任何事情)。

确实,至少有一个SHA-3 "round 2" candidates(ECHO 哈希函数)是围绕 AES 元素构建的,因此 AES-NI 操作码提供了非常显着的性能提升。 (不幸的是,在没有 AES-NI 指令的情况下,ECHO 性能有些糟糕。)

【讨论】:

以上是关于可以使用 CRC32C 作为基础构造一个“好”的哈希函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

在软件中实现 SSE 4.2 的 CRC32C

在软件中实现 SSE 4.2 的 CRC32C

CRC32(C) 可以返回 0 吗?

在 Alpine 上安装 crcmod CRC32C C 扩展

CRC32可以用作散列函数吗?

RV32C指令集