我使用的是Linux x86_64和clang 3.3。
从理论上讲,这可行吗?
std::atomic<__int128_t>
不起作用(对某些函数的引用未定义)。
__atomic_add_fetch
也不起作用('error: cannot compile this atomic library call yet')。
但std::atomic
和__atomic_add_fetch
都适用于64位数字。
我使用的是Linux x86_64和clang 3.3。
从理论上讲,这可行吗?
std::atomic<__int128_t>
不起作用(对某些函数的引用未定义)。
__atomic_add_fetch
也不起作用('error: cannot compile this atomic library call yet')。
但std::atomic
和__atomic_add_fetch
都适用于64位数字。
无法使用单个指令完成此操作,但是您可以模拟它并且仍然是无锁的。除了最早的AMD64处理器之外,x64支持 CMPXCHG16B
指令。通过一些多精度数学运算,您可以很容易地完成此操作。
我不知道GCC中 CMPXCHG16B
的内置函数,但希望您能理解使用 CMPXCHG16B
自旋循环的想法。下面是VC++的一些未测试代码:
// atomically adds 128-bit src to dst, with src getting the old dst.
void fetch_add_128b(uint64_t *dst, uint64_t* src)
{
uint64_t srclo, srchi, olddst[2], exchlo, exchhi;
srchi = src[0];
srclo = src[1];
olddst[0] = dst[0];
olddst[1] = dst[1];
do
{
exchlo = srclo + olddst[1];
exchhi = srchi + olddst[0] + (exchlo < srclo); // add and carry
}
while(!_InterlockedCompareExchange128((long long*)dst,
exchhi, exchlo,
(long long*)olddst));
src[0] = olddst[0];
src[1] = olddst[1];
}
编辑:以下是一些未经测试的代码,基于我所找到的GCC内置函数:
// atomically adds 128-bit src to dst, returning the old dst.
__uint128_t fetch_add_128b(__uint128_t *dst, __uint128_t src)
{
__uint128_t dstval, olddst;
dstval = *dst;
do
{
olddst = dstval;
dstval = __sync_val_compare_and_swap(dst, dstval, dstval + src);
}
while(dstval != olddst);
return dstval;
}
是的,您需要告诉编译器您正在支持它的硬件。
本答案将假定您正在使用x86-64;arm可能有类似的规格。
从通用的x86-64 微架构级别来看,您至少需要 x86-64-v2
以让编译器知道您具有 cmpxchg16b
指令。
这里有一个可工作的godbolt,请注意编译器标志 -march=x86-64-v2
:https://godbolt.org/z/PvaojqGcx
有关x86-64-psABI的更多阅读材料,规范发布在此处。
这是不可能的。没有一条x86-64指令可以在一条指令中完成128位加法,要想做到原子性操作,基本的起点是它必须是单个指令(即使有一些指令在这种情况下也不是原子性的,但那是另一回事)。
你需要在128位数字周围使用其他锁定机制。
编辑:有可能会想出一些使用类似以下内容的东西:
__volatile__ __asm__(
" mov %0, %%rax\n"
" mov %0+4, %%rdx\n"
" mov %1,%%rbx\n"
" mov %1+4,%%rcx\n"
"1:\n
" add %%rax, %%rbx\n"
" adc %%rdx, %%rcx\n"
" lock;cmpxcchg16b %0\n"
" jnz 1b\n"
: "=0"
: "0"(&arg1), "1"(&arg2));
这只是我随手编写的一些代码,并没有进行编译,更不用说验证它是否有效了。但原理是它会一直重复,直到比较相等为止。
编辑2:打字太慢了,Cory Nelson刚刚发布了相同的东西,但使用了内置函数。
编辑3:更新循环以避免读取不需要读取的内存... CMPXCHG16B将为我们完成这项工作。
cmpxcchg16b
前面加上一个lock
吗? - ergohack