为什么世界上会这样定义_mm_crc32_u64(...)
呢?
unsigned int64 _mm_crc32_u64( unsigned __int64 crc, unsigned __int64 v );
“crc32”指令始终累加32位CRC,而不是64位CRC(毕竟是CRC32而不是CRC64)。如果机器指令CRC32恰好有一个64位目标操作数,则忽略上32位,并在完成后用0填充,因此永远没有必要使用64位目标。我理解Intel为了保持一致性而允许指令具有64位目标操作数,但如果我想快速处理数据,我希望源操作数尽可能大(即如果我还有那么多数据则为64位,对于尾部则更小),并且始终使用32位目标操作数。但内置函数不允许64位源和32位目标。请注意其他内置函数:
unsigned int _mm_crc32_u8 ( unsigned int crc, unsigned char v );
"crc"的类型不是8位类型,返回类型也不是,它们都是32位的。为什么没有呢?
unsigned int _mm_crc32_u64 ( unsigned int crc, unsigned __int64 v );
? Intel指令支持此功能,that是最有意义的内部函数。
是否有可移植代码(Visual Studio和GCC)来实现后者的内部函数?谢谢。我的猜测是这样的:
#define CRC32(D32,S) __asm__("crc32 %0, %1" : "+xrm" (D32) : ">xrm" (S))
对于GCC,以及
#define CRC32(D32,S) __asm { crc32 D32, S }
针对VisualStudio。 不幸的是,我对约束条件的工作原理知之甚少,并且在汇编级别的编程语法和语义方面缺乏经验。
小修改:请注意我定义的宏:
#define GET_INT64(P) *(reinterpret_cast<const uint64* &>(P))++
#define GET_INT32(P) *(reinterpret_cast<const uint32* &>(P))++
#define GET_INT16(P) *(reinterpret_cast<const uint16* &>(P))++
#define GET_INT8(P) *(reinterpret_cast<const uint8 * &>(P))++
#define DO1_HW(CR,P) CR = _mm_crc32_u8 (CR, GET_INT8 (P))
#define DO2_HW(CR,P) CR = _mm_crc32_u16(CR, GET_INT16(P))
#define DO4_HW(CR,P) CR = _mm_crc32_u32(CR, GET_INT32(P))
#define DO8_HW(CR,P) CR = (_mm_crc32_u64((uint64)CR, GET_INT64(P))) & 0xFFFFFFFF;
请注意最后一个宏语句的不同之处。缺乏一致性无疑表明内在函数定义得不合理。虽然在最后一个宏中不需要放入显式的
(uint64)
强制转换,但它是隐式的并且确实发生了。反汇编生成的代码显示出了从 32 位到 64 位和从 64 位到 32 位的强制转换代码,这两者都是不必要的。换句话说,它应该是
_mm_crc32_u64
,而不是 _mm_crc64_u64
,但他们实现它的方式却像后者一样。如果我能正确得到上面的
CRC32
定义,那么我将想要更改我的宏。#define DO1_HW(CR,P) CR = CRC32(CR, GET_INT8 (P))
#define DO2_HW(CR,P) CR = CRC32(CR, GET_INT16(P))
#define DO4_HW(CR,P) CR = CRC32(CR, GET_INT32(P))
#define DO8_HW(CR,P) CR = CRC32(CR, GET_INT64(P))