sma*_*tgo 5 c++ macos performance assembly gcc
我想使用bts和bt x86汇编指令来加速Mac上C++代码中的位操作.在Windows上,_bittestandset和_bittest内在函数运行良好,并提供显着的性能提升.在Mac上,gcc编译器似乎不支持这些,所以我试图直接在汇编程序中执行它.
这是我的C++代码(注意'bit'可以> = 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;
}
Run Code Online (Sandbox Code Playgroud)
以下汇编程序代码可以工作,但不是最优的,因为编译器无法优化寄存器分配:
inline void SetBit(LongWord* array, const int bit)
{
__asm {
mov eax, bit
mov ecx, array
bts [ecx], eax
}
}
Run Code Online (Sandbox Code Playgroud)
问题:如何使编译器完全优化bts指令?如何用bt指令替换TestBit?
BTS(和其他BT*insn)具有内存目标的速度很慢.(英特尔> 10 uops).你可能会从地址数学中获得更快的代码来找到正确的字节,并将其加载到寄存器中.然后,您可以使用注册目标执行BT/ BTS并存储结果.
或者可能将a移位1到正确的位置并OR与SetBit的内存目标一起使用,或者AND与内存源一起使用TestBit.当然,如果你避免使用内联asm,编译器可以内联TestBit和使用TEST而不是AND,这在某些CPU上很有用(因为它可以在更多的CPU上进行宏 - 融合到一个测试和分支AND).
这实际上是gcc 5.2从你的C源(memory-dest OR或TEST)生成的.看起来对我来说是最优的(uop比memory-dest少bt).实际上,请注意您的代码是破坏的,因为它假设unsigned long是32位,而不是CHAR_BIT * sizeof(unsigned_long).使用uint32_t,或者char,将是一个更好的计划.需要注意的符号扩展eax到rax与cqde指令,由于它采用了严重-编写的C 1来代替1UL.
另请注意,内联asm不能返回结果(除了使用new-in-gcc v6扩展名!),因此对TestBit使用内联asm可能会导致可怕的代码如:
... ; inline asm
bt reg, reg
setc al ; end of inline asm
test al, al ; compiler-generated
jz bit_was_zero
Run Code Online (Sandbox Code Playgroud)
现代编译器可以并且BT在适当时使用(具有寄存器目的地).最终结果:您的C可能编译为比您建议使用内联asm更快的代码.在被修复为正确且64位清洁之后它会更快.如果你正在优化代码大小,并愿意支付显着的速度惩罚,强制使用bts可能工作,但bt可能仍然不会很好(因为结果进入标志).
inline void SetBit(*array, bit) {
asm("bts %1,%0" : "+m" (*array) : "r" (bit));
}
Run Code Online (Sandbox Code Playgroud)