因此,我有一个设计,其中纳入CRC32C校验和,以确保数据没有被损坏。我决定使用CRC32C,因为如果运行软件的计算机支持SSE 4.2,我可以同时拥有软件版本和硬件加速版本
我将参考英特尔的开发人员手册(第2A卷),该手册似乎提供了crc32
指令背后的算法。不过,我运气不好。英特尔的开发者指南说:
BIT_REFLECT32: DEST[31-0] = SRC[0-31]
MOD2: Remainder from Polynomial division modulus 2
TEMP1[31-0] <- BIT_REFLECT(SRC[31-0])
TEMP2[31-0] <- BIT_REFLECT(DEST[31-0])
TEMP3[63-0] <- TEMP1[31-0] << 32
TEMP4[63-0] <- TEMP2[31-0] << 32
TEMP5[63-0] <- TEMP3[63-0] XOR TEMP4[63-0]
TEMP6[31-0] <- TEMP5[63-0] MOD2 0x11EDC6F41
DEST[31-0] <- BIT_REFLECT(TEMP6[31-0])
现在,据我所知,我已经正确地完成了从TEMP6
开始的所有操作,但我认为我可能误解了多项式除法,或者实现不正确。如果我的理解是正确的,1 / 1 mod 2 = 1
,0 / 1 mod 2 = 0
和两个被零除都是未定义的。
我不明白的是64位和33位操作数的二进制除法是如何工作的。如果SRC
是0x00000000
,而DEST
是0xFFFFFFFF
,则TEMP5[63-32]
将全部是置位,而TEMP5[31-0]
将全部是未置位。
如果我使用TEMP5
中的位作为分子,则会有30次除以0,因为多项式11EDC6F41
只有33位长(因此将其转换为64位无符号整数会留下前30位未设置),因此分母有30位未设置。
然而,如果我使用多项式作为分子,TEMP5
的底部32位未设置,导致在那里除以零,结果的顶部30位将为零,因为分子的顶部30位将为零,如0 / 1 mod 2 = 0
。
我是不是误解了这是怎么回事?只是少了点什么?或者英特尔在他们的文档中遗漏了一些关键步骤?
我之所以去英特尔的开发人员指南寻找他们使用的算法,是因为他们使用的是33位多项式,我想让输出相同,而当我使用32位多项式1EDC6F41
(如下所示)时,这并没有发生。
uint32_t poly = 0x1EDC6F41, sres, crcTable[256], data = 0x00000000;
for (n = 0; n < 256; n++) {
sres = n;
for (k = 0; k < 8; k++)
sres = (sres & 1) == 1 ? poly ^ (sres >> 1) : (sres >> 1);
crcTable[n] = sres;
}
sres = 0xFFFFFFFF;
for (n = 0; n < 4; n++) {
sres = crcTable[(sres ^ data) & 0xFF] ^ (sres >> 8);
}
上面的代码产生4138093821
作为输出,crc32
操作码使用输入0x00000000
产生2346497208
。
对不起,如果这是写得不好或无法理解的地方,这是相当晚了我。
4条答案
按热度按时间ma8fv8wu1#
这里是CRC-32 C的软件和硬件版本。该软件版本经过优化,可一次处理8个字节。硬件版本经过优化,可在单个内核上有效并行运行三条
crc32q
指令,因为该指令的吞吐量为一个周期,但延迟为三个周期。crc32c.c:
生成crc32c.h的代码(stackoverflow不允许我发布表格本身,因为答案中有30,000个字符的限制):
vuv7lop32#
Mark Adler的答案是正确和完整的,但是那些寻求快速和简单的方法来将CRC-32 C集成到他们的应用程序中的人可能会发现适应代码有点困难,特别是如果他们使用Windows和. NET。
根据可用的硬件,我使用硬件或软件方法创建了library that implements CRC-32C。它作为C++和. NET的NuGet包提供。当然是开源的。
除了打包上面的MarkAdler的代码之外,我还发现了一种简单的方法,可以将软件回退的吞吐量提高50%。在我的计算机上,该库现在在软件上达到了2 GB/s,在硬件上达到了20 GB/s。对于那些好奇的人,这里是优化的软件实现:
正如你所看到的,它一次只会咀嚼更大的块。它需要更大的查找表,但它仍然是缓存友好的。表的生成方式相同,只是多了几行。
我探索的一个额外的事情是使用PCLMULQDQ指令来获得AMD处理器上的硬件加速。我已经设法将Intel's CRC patch for zlib(也就是available on GitHub)移植到CRC-32 C多项式,除了神奇常量0x 9db 42487。如果有人能破译这个,请告诉我。在supersaw7's excellent explanation on reddit之后,我还移植了难以捉摸的0x 9db 42487常量,我只需要找一些时间来完善和测试它。
wdebmtf23#
首先,英特尔的
CRC32
指令用于计算CRC-32C
(即使用与常规CRC32不同的多项式)。查看Wikipedia CRC32条目)要使用英特尔的CRC32C硬件加速
gcc
,您可以:1.通过
asm
语句在C代码中使用内联汇编语言1.使用内部函数
_mm_crc32_u8
、_mm_crc32_u16
、_mm_crc32_u32
或_mm_crc32_u64
。参见Intel Intrinsics Guide以了解Intel的编译器icc
的描述,但gcc
也实现了它们。这就是你如何使用
__mm_crc32_u8
,每次占用一个字节,使用__mm_crc32_u64
将进一步提高性能,因为它每次占用8个字节。要编译它,你需要在
CFLAGS
中传递-msse4.2
。像gcc -g -msse4.2 test.c
,否则它会抱怨undefined reference to _mm_crc32_u8
。如果你想恢复到一个普通的C实现,如果指令在运行可执行文件的平台上不可用,你可以使用GCC的
ifunc
属性。喜欢yx2lnoni4#
我在这里比较各种算法:https://github.com/htot/crc32c
最快的算法取自Intels crc_iscsi_v_pcl.asm汇编代码(可在Linux内核中以修改的形式获得),并使用本项目中包含的C Package 器(crcintelasm.cc)。
为了能够在32位平台上运行这些代码,首先它已经被移植到C(crc32intelc),在可能的情况下,需要少量的内联汇编。代码的某些部分取决于位数,crc32q在32位上不可用,movq也不可用,这些都放在宏的(crc32intel.h)中,具有32位平台的替代代码。