具有 PCLMULQDQ 的快速 CRC *未反映*

2024-02-24

我正在尝试写一个PCLMULQDQ 优化的 CRC-32 https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf执行。特定的 CRC-32 变体适用于我不拥有的变体,但我试图以库形式提供支持。在令人惊奇的模型 https://github.com/madler/crcany/blob/master/allcrcs.txt形式,它有以下参数:

width=32 poly=0xaf init=0xffffffff refin=false refout=false xorout=0x00000000 check=0xa5fd3138(省略了我认为是的残留物0x00000000但说实话我不知道那是什么)

该算法的基本非基于表/按位实现(由crcany) is:

uint32_t crc32byond_bit(uint32_t crc, void const *mem, size_t len) {
    unsigned char const *data = mem;
    if (data == NULL)
        return 0xffffffff;
    for (size_t i = 0; i < len; i++) {
        crc ^= (uint32_t)data[i] << 24;
        for (unsigned k = 0; k < 8; k++) {
            crc = crc & 0x80000000 ? (crc << 1) ^ 0xaf : crc << 1;
        }
    }
    return crc;
}

首先,我从根本上不理解描述该算法的论文。我不知道是什么样的东西K1 = [x^(512+64) mod P(x)]方法。 (x是什么?它从哪里来?我不知道。)我是一名专业的软件工程师;我是一名软件工程师。不是学者。坦白说,我根本看不懂这个符号,而且维基百科文章 https://en.wikipedia.org/wiki/Mathematics_of_cyclic_redundancy_checks没有为我做太多事情。也许是我在线性代数方面的弱点再次困扰着我。

我知道一些我希望利用的公共实现:

  • wuffs https://github.com/google/wuffs/blob/main/std/crc32/common_up_x86_sse42.wuffs
  • folly https://github.com/facebook/folly/blob/main/folly/hash/detail/ChecksumDetail.cpp
  • CRC32快速 https://github.com/srijs/rust-crc32fast/blob/master/src/specialized/pclmulqdq.rs

But:

  • 他们使用位反射算法,我不确定如何创建非反射变体。
  • 他们好像不看报纸?例如,wuffs和crc32fast特别指出他们不使用K6,但论文却让K6显得必要。

我发现了一个英特尔实施soft-crc https://github.com/intel/soft-crc/blob/34a84bfd8278ff48e6aa67f0746618242266c8a2/crc.h#L377 but it 似乎没有使用相同的常量 https://github.com/intel/soft-crc/blob/34a84bfd8278ff48e6aa67f0746618242266c8a2/crc.h#L51-L72(K4-K6?μ?)

/**
 * PCLMULQDQ CRC computation context structure
 */
struct crc_pclmulqdq_ctx {
        /**
         * K1 = reminder X^128 / P(X) : 0-63
         * K2 = reminder X^192 / P(X) : 64-127
         */
        uint64_t k1;
        uint64_t k2;

        /**
         * K3 = reminder X^64 / P(X) : 0-63
         * q  = quotient X^64 / P(X) : 64-127
         */
        uint64_t k3;
        uint64_t q;

        /**
         * p   = polynomial / P(X) : 0-63
         * res = reserved : 64-127
         */
        uint64_t p;
        uint64_t res;
};

I believe我需要的聚常数0xAF are:

Px  = 0x1_0000_00AF
k1  = 0x0_5B5A_E0C7
k2  = 0x0_1BD8_1099
k3  = 0x0_1157_936A
k4  = 0x0_1010_1111
k5  = 0x0_0029_5F23
k6  = 0x0_0000_4455
μ   = 0x1_0000_00AF

(来源:打印 crc32-x86-sse42-magic-numbers.go https://github.com/google/wuffs/blob/a55e0a02a2befbd110dd2b76300b4f1caf3d3840/script/print-crc32-x86-sse42-magic-numbers.go with const px = "100000000000000000000000010101111", or 0xaf | (1 << 32))


我希望得到帮助

  1. 了解文章中使用的符号和公式(以及为什么实现似乎与文章有所不同?),
  2. 将现有实现转换为非反射变体,或者也许
  3. 一些伪代码?

我这里有6套16、32、64位crc的代码,非反射和反射。该代码是为 Visual Studio 设置的。注释已添加到英特尔 github 站点上缺少的常量中。

https://github.com/jeffareid/crc https://github.com/jeffareid/crc

32位非反射在这里:

https://github.com/jeffareid/crc/tree/master/crc32f https://github.com/jeffareid/crc/tree/master/crc32f

您需要更改 crc32fg.cpp 中生成常数的多项式。你想要的多项式实际上是:

0x00000001000000afull

我对 crc32fg.cpp 进行了此更改,以在本答案末尾生成 rk.. 常量。

x 是什么?

CRC 使用具有 1 位系数的多项式。例如 0x0B 实际上是 x^3 + x + 1。

XMM寄存器可以读|写16个字节|一次 128 位,但 PCLMULQDQ 只能对两个 64 位操作数进行无进位乘法以产生 128 位乘积。因此,128 位被分成两个 64 位操作数,然后每个操作数乘以一个常数以向前“折叠”。由于 XMM 寄存器可以在一定程度上并行操作,因此使用 8 个 XMM 寄存器来读取 |写入 128 字节 |一次 1024 位。每个折叠步骤“前进”16 个字节 | 128位数据转发128字节| 1024 位,乘以常数。低 64 位乘以 x^(1024) mod poly 以创建“高级”1024 位的 128 位乘积。高 64 位乘以 x^(1024+64) mod poly 以创建高级字节 1024+64 位的 128 位乘积(需要 +64,因为该乘积基于 128 位的高 64 位)数据)。两个 128 位乘积被异或在一起,然后与数据 128 字节 | 进行异或。 1024 位之后在缓冲区中。

请注意,Intel 文档中的示例使用 4 个 XMM 寄存器将数据前进 64 个字节 |一次 512 位,但是我见过的 github 示例和我在 github 存储库中使用的示例使用 8 个 XMM 寄存器并前进 128 个字节 |一次 1024 位。对于支持AVX512的处理器数量相对较少| ZMM寄存器,有前进256字节的例子 |一次 2048 位。我没有配备 AVX512 的计算机,因此我没有任何代码。

由于 XMM 读|写是小端字节序,因此 PSHUFB 用于在每次读取后反转字节。

该代码主要基于使用 65 位多项式的 64 位 CRC,但对于 32 位 CRC,可以通过将较低 32 位设置为零来处理。对于 32 位 CRC,大多数常数只有 32 位,并左移 32 位以简化 PCLMULQDQ 的使用,并进行调整以补偿移位,因此不是 x^(a) mod poly,而是 (x^( a-32) mod 聚)

折叠过程不会产生 CRC,它只是转换数据以推进数据。处理完所有数据后,8 个 XMM 寄存器将使用常量 rk09、rk10、...、rk19、rk20、rk01、rk02 折叠为一个 128 位值。此时(标签 _128_done:)有 128 位数据,并且由于代码基于 64 位 CRC 逻辑,因此逻辑上附加了 64 位零,因此实际上它是一个 192 位值,其中虚构的低 64 位是全部为零。高 64 位向前折叠 128 位,导致 XMM7 中的 128 位值准备好计算 64 位 CRC,但由于这是 32 位 CRC,因此 XMM7 有 96 位数据左移 32 位。高 32 位向前折叠 64 位(在这种情况下,96 位值和 rk06 都左移 32 位,因此在这种情况下 rk06 折叠 64 位(x^64 mod poly)并左移 32 位. 结果是 XMM7 中的 64 位值左移 32 位。

64位数除以33位多项式的商只需要64位数的高32位,因此左移的64位值具有高32位,可以方便计算商。除法实际上是乘以 x^64 / 多项式,PCLMULQDQ 将仅指定使用 XMM7 的高 64 位部分来使用左移 64 位数字的高 32 位。实际的CRC计算基于以下逻辑:

quotient = upper 32 bits of 64 bit value / polynomial
product  = quotient * polynomial
CRC      = 64 bit value XOR product

除法是通过乘以倒数来完成的:x^64 / poly。由于多项式及其逆为 33 位,因此它们无法左移 32 位,因此代码在每次乘法后将乘积左移 4 个字节。 CRC 以 XMM7 的第 32 至 63 位结束,pextrd eax,xmm7,1用于获取那些 32 位。


我修改了 crc32fg.cpp 以使用 CRC 多项式 0x1000000af,这就是我得到的。对于这个多项式,rk07 == rk08,但是对于其他多项式,它们会不同。

rk01    dq      000295f2300000000h      ; x^(32* 3) mod P(x) << 32
rk02    dq      0fafa517900000000h      ; x^(32* 5) mod P(x) << 32
rk03    dq      05cd86bb500000000h      ; x^(32*31) mod P(x) << 32
rk04    dq      0af6f37a300000000h      ; x^(32*33) mod P(x) << 32
rk05    dq      000295f2300000000h      ; x^(32* 3) mod P(x) << 32
rk06    dq      00000445500000000h      ; x^(32* 2) mod P(x) << 32
rk07    dq      000000001000000afh      ; floor(2^64/P(x))
rk08    dq      000000001000000afh      ; P(x)
rk09    dq      09bd57b5d00000000h      ; x^(32*27) mod P(x) << 32
rk10    dq      0b7a4d76400000000h      ; x^(32*29) mod P(x) << 32
rk11    dq      01ae0004200000000h      ; x^(32*23) mod P(x) << 32
rk12    dq      0e7720be600000000h      ; x^(32*25) mod P(x) << 32
rk13    dq      09c7fc8fe00000000h      ; x^(32*19) mod P(x) << 32
rk14    dq      03885faf800000000h      ; x^(32*21) mod P(x) << 32
rk15    dq      0b477ad7100000000h      ; x^(32*15) mod P(x) << 32
rk16    dq      00ac2ae3d00000000h      ; x^(32*17) mod P(x) << 32
rk17    dq      05eae9dbe00000000h      ; x^(32*11) mod P(x) << 32
rk18    dq      0784a483800000000h      ; x^(32*13) mod P(x) << 32
rk19    dq      07d21bf2000000000h      ; x^(32* 7) mod P(x) << 32
rk20    dq      0faebd3d300000000h      ; x^(32* 9) mod P(x) << 32
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有 PCLMULQDQ 的快速 CRC *未反映* 的相关文章

  • ARM 调用约定是否允许函数不将 LR 存储到堆栈中?

    正如标题所示 我在理解 ARM 架构的调用约定时遇到问题 特别是 我仍然很难知道当你调用子程序时 LR 寄存器会发生什么 我认为 当您进入子程序时 处理 LR 寄存器的最明显 最安全的方法是将其存储到堆栈中 但该行为没有出现在文档中 因此我
  • 排列 SSE __m128i 寄存器内的字节

    我有以下问题 In m128i寄存器有 16 个 8bit 值 顺序如下 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 我想要实现的是有效地洗牌字节以获得此排序 1 2 3 4 5 6 7 8 9 10 11
  • 为什么这个 C++ 包装类没有被内联掉?

    EDIT 我的构建系统出了问题 我还在弄清楚到底是什么 但是gcc产生了奇怪的结果 尽管它是 cpp文件 但是一旦我使用了g 然后它按预期工作 对于我一直遇到麻烦的事情来说 这是一个非常精简的测试用例 其中使用数字包装类 我认为会内联 使我
  • 优化数组压缩

    假设我有一个数组k 1 2 0 0 5 4 0 我可以按如下方式计算掩码m k gt 0 1 1 0 0 1 1 0 仅使用掩码 m 和以下操作 左移 右移 And Or 加 减 乘 我可以将 k 压缩为以下形式 1 2 5 4 以下是我目
  • 破坏/分解函数的函数

    我以前有过 here https stackoverflow com questions 4920610 c class function in assembly 已经表明 C 函数不容易用汇编表示 现在我有兴趣以一种或另一种方式阅读它们
  • C++ 中的 CPUID 实现

    我想知道这里是否有人有一些可以从任何托管 net 语言引用的 C CPUID 实现的好示例 另外 如果情况并非如此 我是否应该注意 X86 和 X64 之间的某些实现差异 我想使用 CPUID 来获取运行我的软件的机器上的信息 崩溃报告等
  • Clang 使用 -nostdlib 生成崩溃代码

    我正在尝试为可执行文件设置自己的运行时环境 但无法使用 clang v3 4 1ubuntu1 目标 x86 64 pc linux gnu 来生成没有段错误的可执行文件 我已将问题简化为以下内容 如果我有一个文件 crt1 c 除了满足
  • 为什么 Solaris 汇编器生成的机器代码与 GNU 汇编器在这里不同?

    我为 amd64 编写了这个小汇编文件 对于这个问题来说 代码的作用并不重要 globl fib fib mov edi ecx xor eax eax jrcxz 1f lea 1 rax ebx 0 add rbx rax xchg r
  • NASM 轮班操作员

    您将如何在寄存器上进行 NASM 中的位移位 我读了手册 它似乎只提到了这些操作员 gt gt lt lt 当我尝试使用它们时 NASM 抱怨移位运算符处理标量值 您能解释什么是标量值并举例说明如何使用 gt gt and lt lt 另外
  • 两个基本的 ANTLR 问题

    我正在尝试使用 ANTLR 来获取简单的语法并生成汇编输出 我在 ANTLR 中选择的语言是 Python 许多教程看起来非常复杂或详细阐述与我无关的事情 我真的只需要一些非常简单的功能 所以我有两个问题 将值从一个规则 返回 到另一规则
  • 汇编8086监听键盘中断

    我有与此完全相同的问题 边画边听键盘 https stackoverflow com questions 13970325 8086 listen to keyboard while drawing 但第一个答案 接受的答案 只听键盘一次
  • 寄存器寻址模式与直接寻址模式

    我在试卷中遇到过这个问题 它指出 哪种给定的寻址模式更快 为什么 寄存器寻址方式 直接寻址方式 现在根据我的说法 寄存器寻址模式应该更快 因为寄存器是计算机中最快的存储位置 这是正确答案吗 请帮忙 谢谢 两种寻址模式之间的区别是 地址的来源
  • 在Python中计算结构体的CRC

    我有以下结构 来自 C 中的 NRPE 守护程序代码 typedef struct packet struct int16 t packet version int16 t packet type uint32 t crc32 value
  • 为什么我的空循环在 Intel Skylake CPU 上作为函数调用时运行速度是原来的两倍?

    我正在运行一些测试来比较 C 和 Java 并遇到了一些有趣的事情 在 main 调用的函数中 而不是在 main 本身中 运行具有优化级别 1 O1 的完全相同的基准代码 导致性能大约翻倍 我正在打印 test t 的大小 以毫无疑问地验
  • ARMv8 A64 汇编中立即值的范围

    我的理解是 ARMv8 A64 汇编中的立即参数可以是 12 位长 如果是这样的话 为什么这行汇编代码是 AND X12 X10 0xFEF 产生此错误 使用 gcc 编译时 Error immediate out of range at
  • 程序集比较标志理解

    我正在努力理解汇编程序中的以下代码片段 if EAX gt 5 EBX 1 else EBX 2 在汇编程序中 可以写如下 根据我的书 模拟jge操作说明 https www felixcloutier com x86 jcc您通常会使用
  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • FreePascal x64 上系统单元函数的汇编调用

    我有一些 Delphi 汇编代码 可以在 Win32 Win64 和 OSX 32 上编译并正常工作 XE2 但是 由于我需要它在 Linux 上工作 所以我一直在考虑编译它的 FPC 版本 到目前为止 Win32 64 Linux32 6
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代

随机推荐