【问题标题】:Fast 8-bit checksum algorithm for heterogenous tuples用于异构元组的快速 8 位校验和算法
【发布时间】:2018-07-30 10:30:04
【问题描述】:

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

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

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

【问题讨论】:

  • 这实在是太宽泛了,需要征求意见。你应该测试一下。 Murmur 不错,Spooky 也不错,openSSL 也有不错的。您只需要测试您的实现,查看 Linux 上的 clock_gettime 函数或 windows 上的 QueryPerformanceCounter 来计算散列时间。 (而且校验和和哈希不完全一样,所以要校验和再讨论哈希有点不清楚)
  • @DavidC.Rankin 我不认为 Murmur 或 Spooky 有 8 位实现?
  • 了解,但所有哈希算法(已讨论)都是开源的,因此您可以定制(破解)它们以一次处理一个字节。我只是在看 murmur、yale_hash、djb2 和 fnv1。每次修改一个字节都不会太难。
  • @DavidC.Rankin 感谢您的意见。我希望您看到我的问题不是如何对现有的实现进行基准测试,而是我一开始就没有这样的实现。此外,这不是一次取一个字节,而是自己产生一个 8 位校验和。例如,将它们中的任何一个更改为 SIMD 实现并非易事。
  • Assume all of the values have uniform distribution across all the significant bits ... :: 一切顺利;只需添加它们(这是 IP 校验和所做的)或异或它们。 ... seen of those seem dated因为没有更好的解决方案。

标签: c hash simd checksum


【解决方案1】:

由于您提到所有值都均匀分布在您的所有位中,您可以简单地选择元组中的 any 字节作为 8 位哈希,忽略其余位,即基本免费。结果是一个完全一致的哈希函数,这是最好的(它的冲突概率为 256 分之 1,这是不可预测输入的下限)。

如果您输入的位在某种程度上不均匀,您只需要一个“更好”的哈希函数(对于不仅仅是随机数的真实数据,绝大多数情况下都是这种情况,但我猜您的情况有所不同)。

【讨论】:

    【解决方案2】:

    现代 x86 具有非常快的CRC32C (hardware instruction added in SSE4.2)。通过将 int32 和 int16 连接成一个零扩展的 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 微秒/6 个周期延迟。先给它uint64_t,这样连接uint16和uint32就不是关键路径,即在移位/或和第一个crc32之间创建指令级并行。


    然后水平异或 crc32c 到 8 位

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

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


    或者干脆取CRC32C的低字节。 IDK 如果将所有 4 个字节异或到 1,您将获得多少收益。同样,对于任何多字节哈希函数都是可行的。

    您甚至可以对输入中的所有字节进行水平异或操作。例如加载 16 字节的 SSE2 加载,并屏蔽填充字节,然后将 pshufd / pxor 减少到 8 个字节,pshuflw / pxor 减少到 4 个字节。 然后另一个 pshuflw / pxor 减少到 2 个字节,movd 到整数以进行最终移位/异或。 (或者您可以更早地将 movd 转换为整数,特别是如果编译器有 BMI2 rorx 可以通过一条指令进行复制和移位)。

    【讨论】:

    • 哇。很棒的答案。 _mm_crc32_u64 + XOR 方法在 Haswell E5 (17~ ns) 上以 61-65 个周期完成,具有 2 个 64 位整数和 1 个连接的 64 位整数。为了完整起见,标题是nmmintrin.h
    • @Katie:总是包含immintrin.h 更容易,而不用担心哪个 ISA 扩展与哪个标头搭配。还是您的编译器在 immintrin.h 中不包含 crc32 内部函数?
    • 有趣,software.intel.com/sites/landingpage/IntrinsicsGuide/… 似乎表明我需要来自 nmmintrin.h 的这个,不,我的编译器在 immintrin.h 中不包含 CRC32 内在函数。
    • @Katie: immintrin.h 使用 #include 包含所有以前的标题。使用 gcc/clang/icc/msvc 为我工作:godbolt.org/g/GaKTLB。顺便说一句,请参阅我的最新更新:假​​设 CRC32C 已经充分混合位,您可能只取 CRC32C 的低字节而不是水平异或。
    猜你喜欢
    • 2010-09-12
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多