我需要原子地读/写16个字节。我只使用cmpxchg16进行写入,cmpxchg16可以在所有x64处理器上使用,除了我认为对于一个不起眼的AMD处理器。
现在的问题是对齐的16字节值,只使用cmpxchg16进行修改(它就像一个完整的内存屏障)是否有可能读取一个半字节数据和一半新数据的16字节位置?
只要我用SSE指令读取(因此线程不能在读取过程中被中断),我认为读取看不一致的数据是不可能的(即使在多处理器numa系统中)。我认为它必须是原子的。
我假设当执行cmpxchg16时,它会原子地修改16个字节,而不是通过编写两个8字节块,其他线程可能在其间进行读取(老实说,我不知道它是如何工作的,如果它不是原子的。)
我对吗?如果我错了,有没有办法在不诉诸锁定的情况下进行原子16字节读取?
注意:有一个 这里有类似的问题 但是他们没有处理只用cmpxchg16进行写操作的情况,所以我觉得这是一个单独的,没有答案的问题。
编辑: 其实我认为我的推理是错误的。 SSE加载指令可以作为两个64位读取执行,并且cmpxchg16可以在另一个处理器的两次读取之间执行。
typedef struct
{
unsigned __int128 value;
} __attribute__ ((aligned (16))) atomic_uint128;
unsigned __int128
atomic_read_uint128 (atomic_uint128 *src)
{
unsigned __int128 result;
asm volatile ("xor %%rax, %%rax;"
"xor %%rbx, %%rbx;"
"xor %%rcx, %%rcx;"
"xor %%rdx, %%rdx;"
"lock cmpxchg16b %1" : "=A"(result) : "m"(*src) : "rbx", "rcx");
return result;
}
这应该够了吧。 typedef确保正确对齐。该 cmpxchg16b 需要数据在16字节边界上对齐。
cmpxchg16b将测试是否 *src
包含零,如果是,则写入零(nop)。在任何一种情况下,正确的值将在RAX:RDX之后。
上面的代码评估为简单的事情
push %rbx
xor %rax,%rax
xor %rbx,%rbx
xor %rcx,%rcx
xor %rdx,%rdx
lock cmpxchg16b (%rdi)
pop %rbx
retq