在gcc编译器中使用bts汇编指令 我想使用BTS和BT x86汇编指令来加快我的C++代码在MAC上的位操作。在Windows上,_biteStandSet和_bitestintrinsic运行良好,并提供了显著的性能提升。在Mac上,gcc编译器似乎不支持这些功能,所以我尝试直接在汇编程序中实现 这是我的C++代码(注意‘位’可以是>32):

在gcc编译器中使用bts汇编指令 我想使用BTS和BT x86汇编指令来加快我的C++代码在MAC上的位操作。在Windows上,_biteStandSet和_bitestintrinsic运行良好,并提供了显著的性能提升。在Mac上,gcc编译器似乎不支持这些功能,所以我尝试直接在汇编程序中实现 这是我的C++代码(注意‘位’可以是>32):,c++,performance,macos,gcc,assembly,C++,Performance,Macos,Gcc,Assembly,问题:如何让编译器围绕bts指令进行充分优化?如何用bt指令替换TestBit?另一个稍微间接的回答,GCC从版本4.1开始公开。BTS(以及另一个bt*insns)带有内存目标。通过地址计算找到正确的字节并将其加载到寄存器中,您可能会得到更快的代码。然后,您可以使用注册目的地执行BT/BTS,并存储结果 inline void SetBit(*array, bit) { asm("bts %1,%0" : "+m" (*array) : "r" (bit)); } 或者可以将1移动到

问题:如何让编译器围绕bts指令进行充分优化?如何用bt指令替换TestBit?

另一个稍微间接的回答,GCC从版本4.1开始公开。

BTS
(以及另一个
bt*
insns)带有内存目标。通过地址计算找到正确的字节并将其加载到寄存器中,您可能会得到更快的代码。然后,您可以使用注册目的地执行
BT
/
BTS
,并存储结果

inline void SetBit(*array, bit) {
    asm("bts %1,%0" : "+m" (*array) : "r" (bit));
}
或者可以将
1
移动到正确的位置,并使用
或带有SetBit的内存目标的
或带有
的内存源的
测试位
。当然,如果避免内联asm,编译器可以内联
TestBit
并使用
TEST
,而不是
,这在某些CPU上很有用(因为它可以宏融合到测试中,并在比
更多的CPU上分支)

(内存dest
测试
)。在我看来是最理想的(UOP比内存dest
bt
少)。实际上,请注意,您的代码是中断的,因为它假定
unsigned long
是32位,而不是
CHAR\u BIT*sizeof(unsigned\u long)
。使用
uint32\u t
char
将是一个更好的计划。注意,
cqde
指令将
eax
符号扩展为
rax
,这是由于使用
1
而不是
1UL
的C写得很糟糕

还要注意的是,内联asm无法返回标志作为结果(使用a除外),因此将内联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
可能仍然不会奏效(因为结果会进入标志)。

非常好,谢谢。这帮助我解决了我的第二个问题:内联bool TestBit(const LongWord array[],const int bit){bool flag;asm(“bt%2,%1;setb%0”):“=q”(flag):“m”(*array),“r”(bit));return flag;}@ephemient:请为您的答案添加解释。如果
bit
可能超出0..31范围,内存操作数应该是整个数组,而不仅仅是它的第一个元素。(请记住的疯狂CISC位字符串语义与内存操作数。)此外,还应该让源操作数为
“ri”
,因为它与立即操作数一起工作(这样的速度要慢得多)。但实际上,您根本不应该在现代x86上使用它<带有内存操作数的代码>bts
速度较慢(请参见我的答案)。此指令会损坏C标志,但程序集未指定该标志。不是直接答案,只是指向扩展Asm文档的链接。
BT*
带有内存操作数的速度较慢,因为疯狂的CISC语义。实际上,让编译器发出使用此代码得到的移位/或(或测试)指令序列更快。(至少,这是一个修复了错误的64位干净版本)。实际上,从v6开始,gcc返回标志。@DavidWohlferd:谢谢!有趣的是,他们终于把它包括进去了。@PeterCordes你见过编译器生成bts
?我确实可以得到至少
clang
icc
为“测试位”类型生成
bt
,但对
bts
没有任何运气,即使它对“设置位”似乎非常有用与使用
shl
shlx
以及加载常量
1
等的两个额外操作相比,键入函数。@BeeOnRope:我忘了。您描述的内容听起来很熟悉:使用
bt
测试位,但未能使用
bts
设置位。我很确定这至少在英特尔CPU上是一场胜利,对于像
foo |=1这样的东西,这正是我在godbolt链接中测试的idom。我确实找到了一种方法,仅在特定情况下,在
icc
上生成
bts
。无论是
gcc
还是
clang
都不使用
bts
,并且
icc
在位置可变时不使用它(在位置可变时,它可能更有用,因为非
bts
用于可变移位的代码通常比恒定情况更慢,尤其是在
SHLX
之前)。
inline void SetBit(*array, bit) {
    asm("bts %1,%0" : "+m" (*array) : "r" (bit));
}
...  ; inline asm
bt   reg, reg
setc al       ; end of inline asm
test al, al   ; compiler-generated
jz bit_was_zero