x64 CPU上的原子16字节读取

Atomic 16 byte read on x64 CPUs

本文关键字:字节 读取 CPU x64      更新时间:2023-10-16

我需要以原子方式读/写16个字节。我只使用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

根据参考http://siyobik.info/main/reference/instruction/CMPXCHG8B%2FCMPXCHG16BCCD_ 2在默认情况下不是原子的,但可以通过使用CCD_http://siyobik.info/main/reference/instruction/LOCK

这意味着默认情况下,数据可以在读取和写入阶段进行更改。锁定使读和写都成为原子。