x86 中的原子测试和设置:内联 asm 或编译器生成的锁 bts?
Atomic test-and-set in x86: inline asm or compiler-generated lock bts?
以下代码在为 xeon phi 编译时抛出错误
Error: cmovc is not supported on k1om
.
但它确实可以为常规至强处理器正确编译。
#include<stdio.h>
int main()
{
int in=5;
int bit=1;
int x=0, y=1;
int& inRef = in;
printf("in=%d\n",in);
asm("lock bts %2,%0\ncmovc %3,%1" : "+m" (inRef), "+r"(y) : "r" (bit), "r"(x));
printf("in=%d\n",in);
}
编译器 - icc (ICC) 13.1.0 20130121
相关问题:bit test and set (BTS) on a tbb atomic variable
IIRC,first-gen Xeon Phi 基于 P5 内核(奔腾和奔腾 MMX)。 cmov
直到 P6(又名 Pentium Pro)才被引入。所以我觉得这很正常。
只需让编译器通过编写一个普通的三元运算符来完成它的工作。
其次,cmov
是比 setc
更糟糕的选择,因为您想根据进位标志生成 0 或 1。请参阅下面我的 asm 代码。
另请注意,带有内存操作数的 bts
是 super-slow,因此您不希望它生成该代码,尤其是。在 CPU 上将 x86 指令解码为 uops(就像现代至强)。根据 http://agner.org/optimize/,bts m, r
即使在 P5 上也比 bts m, i
慢得多,所以不要那样做。
只要求编译器将 in
放入寄存器中,或者更好的是,不要为此使用内联汇编。
由于 OP 显然希望它以原子方式工作,因此最好的解决方案是使用 C++11 的 std::atomic::fetch_or
,并将其留给编译器生成 lock bts
.
std::atomic_flag 有一个 test_and_set
功能,但如果有办法将它们紧密打包,我就知道了。也许作为结构中的位域?虽然不太可能。我也没有看到 std::bitset
.
的原子操作
不幸的是,当前版本的 gcc 和 clang 不会从 fetch_or
生成 lock bts
,即使 much-faster immediate-operand 形式可用。我想出了以下 (godbolt link):
#include <atomic>
#include <stdio.h>
// wastes instructions when the return value isn't used.
// gcc 6.0 has syntax for using flags as output operands
// IDK if lock BTS is better than lock cmpxchg.
// However, gcc doesn't use lock BTS even with -Os
int atomic_bts_asm(std::atomic<unsigned> *x, int bit) {
int retval = 0; // the compiler still provides a zeroed reg as input even if retval isn't used after the asm :/
// Letting the compiler do the xor means we can use a m constraint, in case this is inlined where we're storing to already zeroed memory
// It unfortunately doesn't help for overwriting a value that's already known to be 0 or 1.
asm( // "xor %[rv], %[rv]\n\t"
"lock bts %[bit], %[x]\n\t"
"setc %b[rv]\n\t" // hope that the compiler zeroed with xor to avoid a partial-register stall
: [x] "+m" (*x), [rv] "+rm"(retval)
: [bit] "ri" (bit));
return retval;
}
// save an insn when retval isn't used, but still doesn't avoid the setc
// leads to the less-efficient setc/ movzbl sequence when the result is needed :/
int atomic_bts_asm2(std::atomic<unsigned> *x, int bit) {
uint8_t retval;
asm( "lock bts %[bit], %[x]\n\t"
"setc %b[rv]\n\t"
: [x] "+m" (*x), [rv] "=rm"(retval)
: [bit] "ri" (bit));
return retval;
}
int atomic_bts(std::atomic<unsigned> *x, unsigned int bit) {
// bit &= 31; // stops gcc from using shlx?
unsigned bitmask = 1<<bit;
//int oldval = x->fetch_or(bitmask, std::memory_order_relaxed);
int oldval = x->fetch_or(bitmask, std::memory_order_acq_rel);
// acquire and release semantics are free on x86
// Also, any atomic rmw needs a lock prefix, which is a full memory barrier (seq_cst) anyway.
if (oldval & bitmask)
return 1;
else
return 0;
}
如 中所述,xor
/ set-flags / setc
是所有现代 CPU 的最佳序列,当需要结果时一个 0 或 1 的值。我实际上还没有考虑过 P5,但是 setcc
在 P5 上很快所以应该没问题。
当然,如果你想在这个上面分支而不是存储它,inline asm和C之间的边界是一个障碍。花费两条指令来存储一个 0 或 1,只到 test/branch 就可以了,这很愚蠢。
gcc6 的 flag-operand 语法当然值得研究,如果它是一个选项。 (如果您需要针对 Intel MIC 的编译器,则可能不需要。)
以下代码在为 xeon phi 编译时抛出错误
Error: cmovc is not supported on k1om
.
但它确实可以为常规至强处理器正确编译。
#include<stdio.h>
int main()
{
int in=5;
int bit=1;
int x=0, y=1;
int& inRef = in;
printf("in=%d\n",in);
asm("lock bts %2,%0\ncmovc %3,%1" : "+m" (inRef), "+r"(y) : "r" (bit), "r"(x));
printf("in=%d\n",in);
}
编译器 - icc (ICC) 13.1.0 20130121
相关问题:bit test and set (BTS) on a tbb atomic variable
IIRC,first-gen Xeon Phi 基于 P5 内核(奔腾和奔腾 MMX)。 cmov
直到 P6(又名 Pentium Pro)才被引入。所以我觉得这很正常。
只需让编译器通过编写一个普通的三元运算符来完成它的工作。
其次,cmov
是比 setc
更糟糕的选择,因为您想根据进位标志生成 0 或 1。请参阅下面我的 asm 代码。
另请注意,带有内存操作数的 bts
是 super-slow,因此您不希望它生成该代码,尤其是。在 CPU 上将 x86 指令解码为 uops(就像现代至强)。根据 http://agner.org/optimize/,bts m, r
即使在 P5 上也比 bts m, i
慢得多,所以不要那样做。
只要求编译器将 in
放入寄存器中,或者更好的是,不要为此使用内联汇编。
由于 OP 显然希望它以原子方式工作,因此最好的解决方案是使用 C++11 的 std::atomic::fetch_or
,并将其留给编译器生成 lock bts
.
std::atomic_flag 有一个 test_and_set
功能,但如果有办法将它们紧密打包,我就知道了。也许作为结构中的位域?虽然不太可能。我也没有看到 std::bitset
.
不幸的是,当前版本的 gcc 和 clang 不会从 fetch_or
生成 lock bts
,即使 much-faster immediate-operand 形式可用。我想出了以下 (godbolt link):
#include <atomic>
#include <stdio.h>
// wastes instructions when the return value isn't used.
// gcc 6.0 has syntax for using flags as output operands
// IDK if lock BTS is better than lock cmpxchg.
// However, gcc doesn't use lock BTS even with -Os
int atomic_bts_asm(std::atomic<unsigned> *x, int bit) {
int retval = 0; // the compiler still provides a zeroed reg as input even if retval isn't used after the asm :/
// Letting the compiler do the xor means we can use a m constraint, in case this is inlined where we're storing to already zeroed memory
// It unfortunately doesn't help for overwriting a value that's already known to be 0 or 1.
asm( // "xor %[rv], %[rv]\n\t"
"lock bts %[bit], %[x]\n\t"
"setc %b[rv]\n\t" // hope that the compiler zeroed with xor to avoid a partial-register stall
: [x] "+m" (*x), [rv] "+rm"(retval)
: [bit] "ri" (bit));
return retval;
}
// save an insn when retval isn't used, but still doesn't avoid the setc
// leads to the less-efficient setc/ movzbl sequence when the result is needed :/
int atomic_bts_asm2(std::atomic<unsigned> *x, int bit) {
uint8_t retval;
asm( "lock bts %[bit], %[x]\n\t"
"setc %b[rv]\n\t"
: [x] "+m" (*x), [rv] "=rm"(retval)
: [bit] "ri" (bit));
return retval;
}
int atomic_bts(std::atomic<unsigned> *x, unsigned int bit) {
// bit &= 31; // stops gcc from using shlx?
unsigned bitmask = 1<<bit;
//int oldval = x->fetch_or(bitmask, std::memory_order_relaxed);
int oldval = x->fetch_or(bitmask, std::memory_order_acq_rel);
// acquire and release semantics are free on x86
// Also, any atomic rmw needs a lock prefix, which is a full memory barrier (seq_cst) anyway.
if (oldval & bitmask)
return 1;
else
return 0;
}
如 xor
/ set-flags / setc
是所有现代 CPU 的最佳序列,当需要结果时一个 0 或 1 的值。我实际上还没有考虑过 P5,但是 setcc
在 P5 上很快所以应该没问题。
当然,如果你想在这个上面分支而不是存储它,inline asm和C之间的边界是一个障碍。花费两条指令来存储一个 0 或 1,只到 test/branch 就可以了,这很愚蠢。
gcc6 的 flag-operand 语法当然值得研究,如果它是一个选项。 (如果您需要针对 Intel MIC 的编译器,则可能不需要。)