异构元组的快速 8 位校验和算法

Fast 8-bit checksum algorithm for heterogenous tuples

假设我有包含 3 个异构整数类型(int16_tint32_tint64_t)的三元组,我想为这 3 个值计算一个 8 位无符号校验和。假设所有值在所有有效位上均匀分布,因此我们不能通过在连接它们时截断任何值来作弊。

计算具有相对较低冲突率和非加密属性的校验和的快速方法是什么?我猜我可以连接字节并使用 Fletcher 的校验和或 Pearson 散列的变体,但我看到的所有这些实现似乎都过时了,我想看看我是否可以进一步利用任何 SIMD 或属性现代 (Skylake) 架构。

我也知道 MurmurHash,但它没有 8 位实现。

现代 x86 非常快 CRC32C (hardware instruction added in SSE4.2)。将 int32 和 int16 连接成 zero-extended int64_t,并使用两条 CRC32C 指令来累积单个校验和,您可能会得到很好的结果。要让编译器为您执行此操作,请使用 imintrin.h 中的内部函数:unsigned __int64 _mm_crc32_u64( unsinged __int64 crc, unsigned __int64 data ).

根据 Agner Fog's instruction tablescrc32 在 Skylake 上每个时钟吞吐量为 1,延迟为 3 个周期,因此将其输入 2x 8 字节并获得 32 位结果应该只需要 2 uops / 6周期延迟。首先将 uint64_t 提供给它,以便连接 uint16 和 uint32 脱离关键路径,即在 shift/or 和第一个 crc32.

之间创建 instruction-level 并行性

然后将crc32c水平异或到8位:

uint32_t crc = my_object_crc32(&my_object);
crc ^= crc>>16;
crc ^= crc>>8;
crc = (uint8_t)crc;

将更宽的 crc/散列/校验和的位混合成一个 8 位值的水平异或适用于您要使用的任何散列函数。


或者干脆取CRC32C的低字节。 IDK 将所有 4 个字节异或为 1 得到多少(如果有的话)。同样,任何 multi-byte 哈希函数都可行。

您甚至可以水平异或输入中的所有字节。例如加载 16 字节的 SSE2 负载,屏蔽填充字节,然后 pshufd / pxor 减少到 8 个字节,pshuflw / pxor 减少到 4 个字节。 然后另一个 pshuflw / pxor 向下到 2 个字节,并且 movd 到整数以进行最后的移位/异或。 (或者你可以更早地 movd 到整数,特别是如果编译器有 BMI2 rorx 到 copy-and-shift 一条指令)。

既然你提到所有的值都均匀分布在你的所有位上,你可以简单地选择元组中的 any 字节作为你的 8 位哈希,忽略剩余的位,这基本上是免费的。结果是一个完美统一的哈希函数,这是最好的(它的碰撞概率为 256 分之一,这是不可预测输入的下限)。

如果您输入的位以某种方式 non-uniform,您只需要一个 "better" 哈希函数(在绝大多数情况下,真实数据不仅是随机数,而且我猜你的情况不一样)。