以原子方式更新单个位(来自std::bitset
)的最便宜的技术是什么?我不认为x86 BTR
是原子的。
我想知道我是否必须读取最近的字节,然后使用CAS?
如果LOCK
+ BTR
是最快的解决方案,我很乐意接受内联的x86-64汇编答案。
答案 0 :(得分:0)
默认情况下,BTS / BTR不是原子的,但它们可以使用LOCK前缀。
这是我的原子位设置和原子位复位的实现,适用于Intel 64位和32位平台(适用于MSVC,gcc和可能的clang)。
ARM的实施也可以从以下位置获得: http://alice.loria.fr/software/geogram/doc/html/atomics_8h_source.html
/**
* \brief Atomically tests and sets a bit (INTEL only)
* \details Sets bit \p bit of *\p ptr and returns its previous value.
* The function is atomic and acts as a read-write memory barrier.
* \param[in] ptr a pointer to an unsigned integer
* \param[in] bit index of the bit to set in *\p ptr
* \return the previous value of bit \p bit
*/
inline char atomic_bittestandset_x86(volatile unsigned int* ptr, unsigned int bit) {
char out;
#if defined(__x86_64)
__asm__ __volatile__ (
"lock; bts %2,%1\n" // set carry flag if bit %2 (bit) of %1 (ptr) is set
// then set bit %2 of %1
"sbb %0,%0\n" // set %0 (out) if carry flag is set
: "=r" (out), "=m" (*ptr)
: "Ir" (bit)
: "memory"
);
#else
__asm__ __volatile__ (
"lock; bts %2,%1\n" // set carry flag if bit %2 (bit) of %1 (ptr) is set
// then set bit %2 of %1
"sbb %0,%0\n" // set %0 (out) if carry flag is set
: "=q" (out), "=m" (*ptr)
: "Ir" (bit)
: "memory"
);
#endif
return out;
}
/**
* \brief Atomically tests and resets a bit (INTEL only)
* \details Resets bit \p bit of *\p ptr and returns its previous value.
* The function is atomic and acts as a read-write memory barrier
* \param[in] ptr a pointer to an unsigned integer
* \param[in] bit index of the bit to reset in \p ptr
* \return the previous value of bit \p bit
*/
inline char atomic_bittestandreset_x86(volatile unsigned int* ptr, unsigned int bit) {
char out;
#if defined(__x86_64)
__asm__ __volatile__ (
"lock; btr %2,%1\n" // set carry flag if bit %2 (bit) of %1 (ptr) is set
// then reset bit %2 of %1
"sbb %0,%0\n" // set %0 (out) if carry flag is set
: "=r" (out), "=m" (*ptr)
: "Ir" (bit)
: "memory"
);
#else
__asm__ __volatile__ (
"lock; btr %2,%1\n" // set carry flag if bit %2 (bit) of %1 (ptr) is set
// then reset bit %2 of %1
"sbb %0,%0\n" // set %0 (out) if carry flag is set
: "=q" (out), "=m" (*ptr)
: "Ir" (bit)
: "memory"
);
#endif
return out;
}