Gcc 在32位Ubuntu上运行时如何使用汇编POPCNT指令
对于一个特定的项目,我坚持使用gcc和一个运行在i7内核上的32位12.04 LTS Ubuntu,支持多达AVX SIMD指令 由于32位操作系统,我显然无法使用256位上运行的AVX指令。我可以使用128位访问SSE4.2指令,POPCNT可以在16、32和64位数据上运行,所以看起来很有希望。但我尝试了几种方法向POPCNT提供64位数据,但都没有成功。 GCC 4.6.3退货Gcc 在32位Ubuntu上运行时如何使用汇编POPCNT指令,gcc,assembly,optimization,32bit-64bit,hammingweight,Gcc,Assembly,Optimization,32bit 64bit,Hammingweight,对于一个特定的项目,我坚持使用gcc和一个运行在i7内核上的32位12.04 LTS Ubuntu,支持多达AVX SIMD指令 由于32位操作系统,我显然无法使用256位上运行的AVX指令。我可以使用128位访问SSE4.2指令,POPCNT可以在16、32和64位数据上运行,所以看起来很有希望。但我尝试了几种方法向POPCNT提供64位数据,但都没有成功。 GCC 4.6.3退货 r8至r15的“未知寄存器名称” rax rdx的“坏寄存器名” 当试图提供mm寄存器或向我的内联汇编函数提供
- r8至r15的“未知寄存器名称”
- rax rdx的“坏寄存器名”
- 当试图提供mm寄存器或向我的内联汇编函数提供一些uint64或long时,这些寄存器在这种情况下会受到影响
- 写入POPCNTQ会导致“popcnt的指令后缀无效”
这仅仅是因为使用内部函数并不总是提供高效的汇编代码。使用intrinsic快速优化C/C++代码是很好的,如果这足以满足需要,那就好了。但除此之外,与intrinsics相比,我在汇编中使用shuffle编码popcount的性能提高了近30% 我不知道是否有32位popcnt指令,但我敢打赌您不能在32位代码中使用64位popcnt。尝试将a和b声明为uint32\t。顺便说一句,uint64是标准C,uint64不是
popcnt
是一条整数指令。因此,在32位模式下,不能将其用于64位操作数。您需要计算两半的popcnt
,并将它们相加。这就是我测试过的所有叮当版本对内置程序所做的。但是,我无法获得任何gcc版本来使用popcnt指令。因此,虽然通常建议使用内置asm,但在这种情况下,内联asm可能更好。64位POPCOUNT在32位系统上不受支持,因为
REX前缀仅在长模式下可用。(不在32位操作系统中)
因此
写入POPCNTQ会导致“popcnt的指令后缀无效”
请看这里:(引述如下)
解决方法是将64/128位指令拆分为两个/四个32位指令:
; a=uint_64, 64 bit operand, little endian
popcount eax, dword ptr [a]
popcount edx, dword ptr [a+4]
add eax, edx
xor edx, edx ; for first mov below
mov dword ptr [b], edx ; not neccessary, only due to 64 target op (will there ever be 2^64 bits set???)
mov dword ptr [b+4], eax
编辑:MASM32代码中(二进制)汉明距离的64位操作数大小版本:
Hamming_64 PROC word1:QWORD , word2: QWORD
mov ecx, dword ptr [word1]
mov edx, dword ptr [word1+4]
xor ecx, dword ptr [word2]
xor edx, dword ptr [word2+4]
popcnt eax, ecx
popcnt ebx, edx
add eax, ebx ; returns distance in EAX
ret
Hamming_64 ENDP
在使用assembly实现32位POPCNT之后,与SSSE3 shuffle assembly方法相比,似乎没有什么真正的改进。
正如我所怀疑的,只有64位POPCNT版本的速度几乎可以翻倍。是的,有。但与当前使用Sshuffle的SSSE3汇编实现相比,我将错过一大部分(全部?)改进。写它只是为了说明我是如何将参数传递给汇编函数寄存器的。好吧,这很尴尬,我可以使用SSEx在32位操作系统上处理多达128位的压缩数据,而汇编POPCNT无法处理64位数据,即使打包:-s@user3581220为什么它在32位模式下工作在64位值上,而那里甚至没有64位寄存器?因为在32位操作系统上有128位xmm寄存器可访问(考虑打包的值并不是什么大问题).@user3581220英特尔决定使XMM寄存器在处理器的所有操作模式下都可访问,但AMD决定只允许在64位长模式下访问整数寄存器的完整64位。@user3581220 XMM寄存器不是用于大128位数字,而是用于同时存储多个值。它们有不同的用法,因为XMM不是通用寄存器。此外,使用64位寄存器需要REX前缀,该前缀采用inc和dec的操作码。这就是为什么不能在64位模式下使用单字节inc/dec。原因是x86中几乎没有前缀和操作码的代码点。我以前在镜像站点上看到过此文档,但我想确定没有人找到使用64位版本的方法。嗯,我目前的组装方法实际上是对两个64字节向量进行异或运算,然后处理汉明距离,因此我尝试用两种不同的方式将洗牌替换为32位POPCNT:a)在处理POPCNT之前将XOR结果从xmm写回对齐数组,b)将XOR结果xmm寄存器移位4个字节,并将其置于32位exx寄存器中,然后在该寄存器上处理POPCNT。将代码中使用的寄存器延迟考虑到指令延迟和吞吐量,我无法改进shuffle方法(实际上我损失了5%到15%)。我仍然不确定您要实现什么或尝试实现什么算法。无论如何,我在文章中添加了一个32位版本,它计算两个64位值(QWord)的汉明距离,而不使用xmm寄存器。很直接,应该很快。我也试过了。使用SSSE3 shuffle与popcount的差异仅为1-2%。不过,这次我测试了在每个内核上运行OpenMP,一个线程用于使用32位GP寄存器的32位XOR和popcount,另一个线程用于使用xmm寄存器的128位XOR和SSSE3 shuffle popcount。一个核心进程在for循环中为线程拆分200.000个向量,首先为[0,100.000]和[100.000,200.000],然后为(i=0;iI)可能是错误的,但在32位模式下RAX和R15的确切含义是什么?它们是GP 64位寄存器的名称,根据定义,在32位模式下没有。宽寄存器是向量“XMM”寄存器。虽然
“r”(a)
似乎是一个无辜的语法,但它确实要求a
适合GP寄存器。请注意,您正在使用
; a=uint_64, 64 bit operand, little endian
popcount eax, dword ptr [a]
popcount edx, dword ptr [a+4]
add eax, edx
xor edx, edx ; for first mov below
mov dword ptr [b], edx ; not neccessary, only due to 64 target op (will there ever be 2^64 bits set???)
mov dword ptr [b+4], eax
Hamming_64 PROC word1:QWORD , word2: QWORD
mov ecx, dword ptr [word1]
mov edx, dword ptr [word1+4]
xor ecx, dword ptr [word2]
xor edx, dword ptr [word2+4]
popcnt eax, ecx
popcnt ebx, edx
add eax, ebx ; returns distance in EAX
ret
Hamming_64 ENDP