使用 BLAKE2 在 Golang 中进行快速哈希

在这篇博文中,我们想介绍一个优化实现,blake2b-simd ,它是 BLAKE2 哈希算法的纯 Go 实现,利用了 SIMD 指令。与(非汇编)Go 实现相比,它提高了4 倍的速度,并且在高端 CPU 上可以实现接近每核心 1 GB/秒的哈希速度。与标准 SHA512 实现相比,它大约提高了 3 倍的性能。

提供三种类型,即 AVX2、AVX 和 SSE(自动选择最佳类型)。以下列表显示了它们与(非汇编)Go 实现的比较情况:

  • AVX2:3.94 倍
  • AVX:3.28 倍
  • SSE:2.85 倍

与其他哈希技术的比较

以下是几种哈希技术的比较,以及它们以 MB/s 衡量的处理速度和 BLAKE2b(AVX2 版本)的性能提升。

MD5    : 607.48 MB/s  (1.4x)
SHA1   : 522.94 MB/s  (1.6x)
SHA256 : 189.58 MB/s  (4.5x)
SHA512 : 306.33 MB/s  (2.8x)
BLAKE2b: 850.64 MB/s

位腐蚀保护

Minio(XL 版本)中,我们使用 擦除编码 来防止数据丢失。对象被分割成(默认情况下)8 个数据块,与 8 个奇偶校验块组合到总共 16 个硬盘上。这意味着即使(极不可能的情况下)丢失了 8 个硬盘,仍然可以重建原始对象并将其返回给用户(注意硬盘可以被替换,然后会自动重建)。

为了防止 位腐蚀(可能会随着时间的推移发生),Minio 通过计算块的哈希值并将其与最初计算的哈希值进行比较,来验证从磁盘读取回来的任何数据块的完整性。只有在两个哈希值相同的情况下,才能保证数据没有被更改(因此可以安全地返回给客户端)。如果哈希值之间有任何差异,块本身将被丢弃,必须从其他数据和奇偶校验块中重建。

由于此验证步骤是一个非常频繁的操作,很明显哈希技术的性能对整体系统性能有很大影响。因此,Minio 投入了尽可能快且可靠的哈希技术,并发现 BLAKE2 是一个不错的选择。

还有谁适合使用?

除了位腐蚀保护之外,还有许多其他软件用例需要快速哈希。您可以考虑存储系统中的重复数据删除、入侵检测、版本控制系统和完整性检查。在这些应用程序中,哈希也是一项非常频繁的任务,因此这里获得的任何性能提升都将对整体性能产生重大影响。

asm2plan9s

初始版本基于 BLAKE2 中的参考 SSE 实现。由于 Go 汇编对 AVX/SSE 指令的支持不完全,我们开发了一个名为 asm2plan9s 的简单工具,它有助于将指令组装成它们的字节码等效项。

在后台,此工具使用 yasm 来逐个组装每个指令,并将获得的字节码插入到源代码文件中。

例如,考虑以下 AVX 指令(保存在文件 example.s 中)

// VPADDQ  XMM0,XMM1,XMM8

通过运行$ asm2plan9s example.s,该文件将被转换为

LONG $0xd471c1c4; BYTE $0xc0   // VPADDQ  XMM0,XMM1,XMM8

根据指令,将会有更多或更少的 LONG、WORD 和/或 BYTE。此外,它支持在定义中使用,有关更多信息,请参阅 GitHub 上的项目。

技术细节

BLAKE2b 是一种在 64 位整数上操作的哈希算法。AVX2 版本使用 256 位宽的 YMM 寄存器,以便基本上并行处理四个“Golang”操作。AVX 和 SSE 在 128 位 XMM 寄存器上操作(两个操作并行)。以下是文件 compressAvx2_amd64.s、compressAvx_amd64.s 和 compress_generic.go 的源代码摘录。

AVX2

VPADDQ  YMM0,YMM0,YMM1   /* v0 += v4,v1 += v5,v2 += v6,v3 += v7 */

AVX

VPADDQ  XMM0,XMM0,XMM2   /* v0 += v4, v1 += v5 */VPADDQ  XMM1,XMM1,XMM3   /* v2 += v6, v3 += v7 */

Golang

v0 += v4
v1 += v5
v2 += v6
v3 += v7

指令中显示的并行性将很清楚。由于其他约束(如内存访问、缓存等),从 AVX 到 AVX2 等时,不会获得完整的(理论)2 倍改进,但随着时间的推移,差异实际上可能会扩大。(请注意,现在的编译器也尝试对操作进行矢量化。)

结论

我们希望您能发现此项目有用。前往 GitHub 上的 blake2b-simd 进行查看。请告诉我们您的想法。