Geo*_*xon 8 c assembly crc32 zlib mathematical-optimization
我最近一直在使用CloudFlare优化的zlib,结果非常令人印象深刻.
不幸的是,他们似乎已经假设zlib的开发被放弃了,他们的分叉也就此消失了.我最终能够手动将他们的更改改为当前的zlib开发分支,尽管这是一个真正的痛苦.
无论如何,我还没有能够利用CloudFlare代码中的一个主要优化,即使用新的(Haswell和后来的,我相信)英特尔处理器中包含的PCLMULQDQ无进位乘法指令实现的快速CRC32代码,因为:
我在Mac上,并没有clang集成汇编程序或Apple的古代GAS都不了解所用的新GAS助记符,以及
代码从Linux内核中取出并且是GPL2,这使得整个库GPL2,从而基本上使它对我的目的无用.
所以我做了一些狩猎,几个小时之后我偶然发现了一些Apple在他们的bzip2中使用的代码:arm64和x86_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.
Run Code Online (Sandbox Code Playgroud)
但不幸的是,经过几次尝试,此时我似乎有点过头了.而且我不确定如何真正做到这一点.所以我希望有人能告诉我如何/在哪里调用所提供的功能.
(如果有一种方法可以在运行时检测到必要的功能,并且如果硬件功能不可用则可以回退到软件实现,这也是很棒的,所以我不必分发多个二进制文件. ,至少,如果有人可以帮助我了解如何让库正确使用基于Apple PCLMULQDQ的CRC32,那么无论如何都会有很长的路要走.)
正如它所说,您需要在长度为 16 字节倍数的 16 字节对齐缓冲区上计算 CRC 和。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;
Run Code Online (Sandbox Code Playgroud)