使用bts汇编指令和gcc编译器

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?

Pet*_*des 7

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 ORTEST)生成的.看起来对我来说是最优的(uop比memory-dest少bt).实际上,请注意您的代码是破坏的,因为它假设unsigned long是32位,而不是CHAR_BIT * sizeof(unsigned_long).使用uint32_t,或者char,将是一个更好的计划.需要注意的符号扩展eaxraxcqde指令,由于它采用了严重-编写的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可能仍然不会很好(因为结果进入标志).

  • 实际上,从v6开始,gcc [can](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#FlagOutputOperands)返回标志. (4认同)
  • @BeeOnRope:我忘记了.你所描述的听起来很熟悉:使用`bt`测试位,但没有使用`bts`来设置位.对于像`foo | = 1 << n`这样的东西,我非常肯定这是英特尔CPU的胜利,其中`n`不是编译时常量,foo已经在寄存器中.是的,刚刚测试过,没有运气:https://godbolt.org/g/9s6i9D (2认同)

eph*_*ent 5

inline void SetBit(*array, bit) {
    asm("bts %1,%0" : "+m" (*array) : "r" (bit));
}
Run Code Online (Sandbox Code Playgroud)