如何在zlib CRC32中正确使用无进位乘法组件(PCLMULQDQ)?

我最近一直在玩 CloudFlare’s optimized zlib,结果非常令人印象深刻.

不幸的是,他们似乎已经假设zlib的开发被抛弃了,他们的分叉也没了.我最终能够manually rebase their changescurrent zlib development分支,虽然这是一个真正的痛苦的屁股.

无论如何,我还没有能够利用CloudFlare代码中的一个主要优化,即fast CRC32 code implemented with the PCLMULQDQ无人乘法指令包含更新的(Haswell和后来的,我相信)英特尔处理器,因为:

>我在Mac上,并没有clang集成汇编程序和Apple的古代GAS都不了解所用的新GAS助记符,

>代码从Linux内核中取出并且是GPL2,这使得整个库GPL2,从而基本上使它对我的目的无用.

所以我做了一些狩猎,几个小时之后我偶然发现了苹果公司在他们的bzip2中使用的一些代码:arm64x86_64的手写,矢量化CRC32实现.

奇怪的是,x86_64程序集的注释(仅)在arm64源代码中,但它似乎表明此代码可以与zlib一起使用:

This function SHOULD NOT be called directly. It should be called in a wrapper
function (such as crc32_little in crc32.c) that 1st align an input buffer to 16-byte (update crc along the way),
and make sure that len is at least 16 and SHOULD be a multiple of 16.

但不幸的是,经过几次尝试,此时我似乎有点过头了.而且我不确定如何真正做到这一点.所以我希望有人能告诉我如何/在何处调用所提供的功能.

(如果有一种方法可以在运行时检测到必要的功能,并且如果硬件功能不可用则可以回退到软件实现,那也很棒,所以我不必分发多个二进制文件.但是,至少,如果有人可以帮我解决如何让图书馆正确使用基于Apple PCLMULQDQ的CRC32,那么无论如何都会有很长的路要走.)

如上所述,您需要在16字节对齐的缓冲区上计算CRC总和,该缓冲区的长度为16个字节的倍数.因此,您将当前缓冲区指针转换为uintptr_t,并且只要其4个LSB位不为零,就会增加将字节输入普通CRC-32例程的指针.一旦你处于16字节对齐的地址,你将剩余的长度向下舍入到16的倍数,然后将这些字节提供给快速CRC-32,并将剩余的字节再次提供给慢速计算.

就像是:

// a function for adding a single byte to crc
uint32_t crc32_by_byte(uint32_t crc, uint8_t byte);

// the assembly routine
uint32_t _crc32_vec(uint32_t crc, uint8_t *input, int length);

uint32_t crc = initial_value;
uint8_t *input = whatever;
int length = whatever; // yes, the assembly uses *int* length.

assert(length >= 32); // if length is less than 32 just calculate byte by byte
while ((uintptr_t)input & 0xf) { // for as long as input is not 16-byte aligned
    crc = crc32_by_byte(crc, *input++);
    length--;
}

// input is now 16-byte-aligned
// floor length to multiple of 16
int fast_length = (length >> 4) << 4;
crc = _crc32_vec(crc, input, fast_length);

// do the remaining bytes
length -= fast_length;
while (length--) {
    crc = crc32_by_byte(crc, *input++)
}
return crc;
相关文章
相关标签/搜索