如何在C ++中以原子方式添加和获取128位数字?

时间:2013-08-11 23:10:47

标签: c++ atomic

我使用Linux x86_64和clang 3.3。

这在理论上是否可行?

std::atomic<__int128_t>不起作用(对某些函数的未定义引用)。

__atomic_add_fetch也不起作用('错误:无法编译此原子库调用')。

std::atomic__atomic_add_fetch都可以使用64位数字。

2 个答案:

答案 0 :(得分:8)

使用单个指令无法执行此操作,但您可以模拟它并仍然无锁。除最早的AMD64 CPU外,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;
}

答案 1 :(得分:2)

这是不可能的。没有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));

这只是我刚刚被攻击的东西,我没有编译它,更不用说验证它会起作用。但原则是它重复直到比较平等。

Edit2:Darn打字太慢了,Cory Nelson刚刚发布了同样的东西,但是使用了内插。

Edit3:更新循环到不需要读取的不必要的读取内存... CMPXCHG16B为我们做到了。