我需要原子地读/写16个字节。我只使用cmpxchg16进行写入,cmpxchg16可用于所有x64处理器,但我认为只有一个不起眼的AMD处理器。
现在的问题是对齐的16字节值,只使用cmpxchg16进行修改(它就像一个完整的内存屏障)是否有可能读取一个16字节的位置,即半旧数据和一半新数据?
只要我使用SSE指令读取(因此线程不能在读取过程中被中断)我认为读取不可能的数据是不可能的(即使在多处理器numa系统中)。我认为它必须是原子的。
我假设当执行cmpxchg16时,它会原子地修改16个字节,而不是通过编写两个8字节块,其他线程可能会在其间进行读取(老实说,我不知道如何如果它不是原子的话,它可以工作。)
我是对的吗?如果我错了,有没有办法在不诉诸锁定的情况下进行原子16字节读取?
注意:有一个couple similar questions here,但他们不会处理仅使用cmpxchg16进行写入的情况,所以我觉得这是一个单独的,未回答的问题。
编辑:其实我认为我的推理是错误的。 SSE加载指令可以作为两个64位读取执行,并且cmpxchg16可以在两次读取之间由另一个处理器执行。
答案 0 :(得分:9)
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
答案 1 :(得分:1)
根据引用http://siyobik.info/main/reference/instruction/CMPXCHG8B%2FCMPXCHG16B,CMPXCHG16
默认情况下不是原子的,但可以使用LOCK
http://siyobik.info/main/reference/instruction/LOCK
这意味着默认情况下,可以在读写阶段更改数据。锁定使读取和写入都成为原子。