我想使用 bts 和 bt x86 汇编指令来加速我在 Mac 上的 C++ 代码中的位操作。在 Windows 上,_bittestandset 和 _bittest 内部函数运行良好,并提供显着的性能提升。在 Mac 上,gcc 编译器似乎不支持这些,所以我尝试直接在汇编程序中进行。
这是我的 C++ 代码(请注意,“位”可以 >= 32):
typedef unsigned long LongWord;
#define DivLongWord(w) ((unsigned)w >> 5)
#define ModLongWord(w) ((unsigned)w & (32-1))
inline void SetBit(LongWord array[], const int bit)
{
array[DivLongWord(bit)] |= 1 << ModLongWord(bit);
}
inline bool TestBit(const LongWord array[], const int bit)
{
return (array[DivLongWord(bit)] & (1 << ModLongWord(bit))) != 0;
}
以下汇编代码有效,但不是最优的,因为编译器无法优化寄存器分配:
inline void SetBit(LongWord* array, const int bit)
{
__asm {
mov eax, bit
mov ecx, array
bts [ecx], eax
}
}
问题:如何让编译器围绕 bts 指令进行全面优化?以及如何用 bt 指令替换 TestBit?
最佳答案
BTS
(和其他 BT*
insns)具有内存目标 are slow. (>10 uops on Intel) .您可能会通过地址数学计算找到正确的字节并将其加载到寄存器中来获得更快的代码。然后你可以做 BT
/BTS
与注册目的地和存储结果。
或者转移一个1
到正确的位置并使用OR
带有 SetBit 的内存目标,或 AND
带有 TestBit
的内存源.当然,如果你避免内联 asm,编译器可以内联 TestBit
并使用 TEST
而不是 AND
,这在某些 CPU 上很有用(因为它可以在比 AND
更多的 CPU 上宏融合到测试和分支中)。
This is in fact what gcc 5.2 generates from your C source (内存目标 OR
或 TEST
)。对我来说看起来是最佳的(比 memory-dest bt
更少的微指令)。实际上,请注意您的代码已损坏,因为它假定 unsigned long
是 32 位,不是 CHAR_BIT * sizeof(unsigned_long)
.使用 uint32_t
, 或 char
,将是一个更好的计划。注意 eax
的符号扩展进入rax
与 cqde
指令,由于使用 1
的 C 写得不好而不是 1UL
.
另请注意,内联 asm 无法返回标志作为结果(new-in-gcc v6 extension! 除外),因此将内联 asm 用于 TestBit 可能会导致糟糕的代码,例如:
... ; inline asm
bt reg, reg
setc al ; end of inline asm
test al, al ; compiler-generated
jz bit_was_zero
现代编译器可以并且确实使用 BT
在适当的时候(有一个注册目的地)。最终结果:您的 C 可能编译成比您建议使用内联 asm 执行的代码更快的代码。在错误修正为正确和 64 位清洁后,它会更快。如果您正在优化代码大小,并且愿意付出显着的速度代价,则强制使用 bts
可以工作,但是 bt
可能仍然无法正常工作(因为结果进入标志)。
关于c++ - 在 gcc 编译器中使用 bts 汇编指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1983303/