ARMV8 中的数据竞争安全吗?
is data race safe in ARMV8?
众所周知,INTEL X86 架构中访问对齐的基本数据类型是原子的。 ARMV8呢?
我试图从 Arm Architecture Reference Manual Armv8 中获取结果,对于 A-profile 架构,我确实找到了与原子性相关的内容。 ARMV8是other-multi-copy atomic。它承诺多线程访问同一个 LOCATION 是原子的。但它说 LOCATION 是一个字节。我想知道如果线程 1 在没有锁定的情况下写入对齐的 uint64_t 内存,而线程 2 同时在没有锁定的情况下读取或写入它。是原子的吗?(uint64_t是8个字节,而LOCATION只有一个字节)
这在 ARMv8 体系结构参考手册的 B2.2 中有解释。一般来说,最多 64 位的普通加载和存储,如果自然对齐,是 单拷贝原子。特别是,如果一个线程存储到一个地址,而另一个线程加载同一地址,则可以保证加载看到的是旧值或新值,而不会出现撕裂或其他未定义的行为。这大致类似于 C 或 C++ 中的 relaxed
加载或存储;事实上,您可以看到编译器为此类原子访问发出普通的加载和存储指令。 https://godbolt.org/z/cWjaed9rM
我们举个例子来证明一下。为简单起见,让我们使用对齐的 2 字节半字 H,将其字节称为 H0 和 H1。假设在遥远的过去,H被一条存储指令Wi初始化为0x0000;对字节 H0 和 H1 的相应写入将表示为 Wi.0 和 Wi.1。现在让新的存储指令 Wn = {Wn.0,Wn.1} 存储值 0xFFFF,并让它与加载指令 R = {R.0,R.1} 竞争。 B2.2.1,前两个项目符号,每个访问 Wi、Wn、R 都是单拷贝原子。我们希望证明 R.0,R.1 都 return 0x00,或者它们都 return 0xFF.
通过 B2.3.2 有一个 reads-from 关系将每个读取与一些写入配对。 R.0 必须 从 读取 Wi.0 或 Wn.0,因为它们是对 H0 的仅有的两次写入,因此它必须 return 0x00 或 0xFF。同样,R.1 也必须 return 0x00 或 0xFF。如果它们都是 return 0x00 我们就完成了,所以假设其中一个,比如 R.1,returns 0xFF,让我们证明 R.0 也 returns 0xFF。
我们假设 R.1 读取自 Wn.1。通过 B2.2.2 (2)、none
在 B2.3.2 的意义上,由 Wn 生成的重叠写入的 coherence-after 由 R 生成的相应重叠读取。特别是,Wn.0 在 R.0.
之后不相干
请注意,Wn.0 是 coherence-after Wi.0(coherence order 是写入的总顺序,因此必须一个接一个,我们假设 Wi 发生在很久以前,其间有足够的顺序或同步)。因此,如果 R.0 reads-from Wi.0,则 Wn.0 是 coherence-after R.0(定义 coherence-after,第二句)。我们只是争辩说情况并非如此,因此 R.0 不会 read-from Wi.0;它必须 读取自 Wn.0,因此 return 0xFF。 ∎
请注意,在 x86 上,普通加载和存储隐式分别带有获取和释放顺序,而在 ARM64 上则不然。你必须为此使用 ldar / stlr
。
众所周知,INTEL X86 架构中访问对齐的基本数据类型是原子的。 ARMV8呢? 我试图从 Arm Architecture Reference Manual Armv8 中获取结果,对于 A-profile 架构,我确实找到了与原子性相关的内容。 ARMV8是other-multi-copy atomic。它承诺多线程访问同一个 LOCATION 是原子的。但它说 LOCATION 是一个字节。我想知道如果线程 1 在没有锁定的情况下写入对齐的 uint64_t 内存,而线程 2 同时在没有锁定的情况下读取或写入它。是原子的吗?(uint64_t是8个字节,而LOCATION只有一个字节)
这在 ARMv8 体系结构参考手册的 B2.2 中有解释。一般来说,最多 64 位的普通加载和存储,如果自然对齐,是 单拷贝原子。特别是,如果一个线程存储到一个地址,而另一个线程加载同一地址,则可以保证加载看到的是旧值或新值,而不会出现撕裂或其他未定义的行为。这大致类似于 C 或 C++ 中的 relaxed
加载或存储;事实上,您可以看到编译器为此类原子访问发出普通的加载和存储指令。 https://godbolt.org/z/cWjaed9rM
我们举个例子来证明一下。为简单起见,让我们使用对齐的 2 字节半字 H,将其字节称为 H0 和 H1。假设在遥远的过去,H被一条存储指令Wi初始化为0x0000;对字节 H0 和 H1 的相应写入将表示为 Wi.0 和 Wi.1。现在让新的存储指令 Wn = {Wn.0,Wn.1} 存储值 0xFFFF,并让它与加载指令 R = {R.0,R.1} 竞争。 B2.2.1,前两个项目符号,每个访问 Wi、Wn、R 都是单拷贝原子。我们希望证明 R.0,R.1 都 return 0x00,或者它们都 return 0xFF.
通过 B2.3.2 有一个 reads-from 关系将每个读取与一些写入配对。 R.0 必须 从 读取 Wi.0 或 Wn.0,因为它们是对 H0 的仅有的两次写入,因此它必须 return 0x00 或 0xFF。同样,R.1 也必须 return 0x00 或 0xFF。如果它们都是 return 0x00 我们就完成了,所以假设其中一个,比如 R.1,returns 0xFF,让我们证明 R.0 也 returns 0xFF。
我们假设 R.1 读取自 Wn.1。通过 B2.2.2 (2)、none 在 B2.3.2 的意义上,由 Wn 生成的重叠写入的 coherence-after 由 R 生成的相应重叠读取。特别是,Wn.0 在 R.0.
之后不相干请注意,Wn.0 是 coherence-after Wi.0(coherence order 是写入的总顺序,因此必须一个接一个,我们假设 Wi 发生在很久以前,其间有足够的顺序或同步)。因此,如果 R.0 reads-from Wi.0,则 Wn.0 是 coherence-after R.0(定义 coherence-after,第二句)。我们只是争辩说情况并非如此,因此 R.0 不会 read-from Wi.0;它必须 读取自 Wn.0,因此 return 0xFF。 ∎
请注意,在 x86 上,普通加载和存储隐式分别带有获取和释放顺序,而在 ARM64 上则不然。你必须为此使用 ldar / stlr
。