【问题标题】:How to properly use carry-less multiplication assembly (PCLMULQDQ) in zlib CRC32?如何在 zlib CRC32 中正确使用无进位乘法程序集 (PCLMULQDQ)?
【发布时间】:2016-09-19 07:35:38
【问题描述】:

我最近一直在玩CloudFlare's optimized zlib,结果确实令人印象深刻。

不幸的是,他们似乎认为 zlib 的开发已被放弃,并且他们的分叉分离了。我最终能够将manually rebase their changes 转到current zlib development 分支,尽管这真的很痛苦。

无论如何,CloudFlare 代码中还有一个 主要 优化我无法使用,即 fast CRC32 code implemented with the PCLMULQDQ 包含在较新版本(Haswell 和更高版本,我相信)英特尔处理器,因为:

  1. 我在 Mac 上,clang 集成汇编器和苹果古老的 GAS 都不理解使用的较新的 GAS 助记符, 和

  2. 代码是从 Linux 内核提取的,是 GPL2,这使得整个库 GPL2,因此基本上使它对我的目的毫无用处。

所以我四处寻找,几个小时后,我偶然发现了 Apple 在其 bzip2 中使用的一些代码:arm64 和 x86_64 的手写矢量化 CRC32 实现。

奇怪的是,x86_64 程序集的 cmets (仅)在 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,那将有很长的路要走,无论如何。)

【问题讨论】:

  • 您可以在运行时使用 CPUID 指令枚举硬件功能。查看英特尔的文档。

标签: c assembly mathematical-optimization zlib crc32


【解决方案1】:

正如它所说,您需要在长度为 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;

【讨论】:

    猜你喜欢
    • 2015-03-18
    • 2022-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多