最大的数据类型,可以原子获取-ANDed?

时间:2015-06-19 23:48:04

标签: c++ vector atomic avx

我想尝试使用类似的东西原子重置256位:

#include <x86intrin.h>
#include <iostream>
#include <array>
#include <atomic>

int main(){

    std::array<std::atomic<__m256i>, 10> updateArray;

    __m256i allZeros = _mm256_setzero_si256();

    updateArray[0].fetch_and(allZeros);
}

但是我遇到了关于没有fetch_and()的元素的编译器错误。这是不可能的,因为256位类型太大而不能保证原子性?

我还有其他方法可以实现吗?我正在使用GCC。

如果没有,我可以原子复位64位的最大类型是什么?

编辑:任何AVX指令都能以原子方式执行fetch-AND吗?

2 个答案:

答案 0 :(得分:5)

所以有一些不同的事情需要解决:

  1. 处理器可以做什么?
  2. 我们原子意味着什么?
  3. 您能让编译器生成处理器可以执行的代码吗?
  4. C ++ 11/14标准是否支持?
  5. 对于#1和#2:

    在x86中,有指令可以执行8,16,32,64,128,256和512位操作。一个处理器[至少如果数据与它自己的大小对齐]将以原子方式执行该操作。但是,对于“真正的原子”操作,它还需要防止更新数据中的竞争条件[换句话说,阻止某些其他处理器读取,修改和写回相同的位置]。除了少量的“隐含锁定”指令之外,这是通过向特定指令添加“锁定前缀”来完成的 - 这将对系统中的其他处理器执行正确类型的缓存 - 技术术语以确保只有这个处理器可以更新这些数据。

    我们不能使用带有LOCK前缀的VEX指令(来自英特尔的手册)

      

    在VEX之前具有LOCK前缀的任何VEX编码指令都将为#UD

    你需要一个VEX前缀来使用AVX指令,而#UD意味着“未定义指令” - 换句话说,如果我们尝试执行它,代码将导致处理器异常。

    因此,100%确定处理器不能一次对256位进行原子操作。这个答案讨论了SSE指令的原子性: SSE instructions: which CPUs can do atomic 16B memory operations?

    如果指令无效,

    #3是没有意义的。

    #4 - 好吧,标准支持std::atomic<uintmax_t>,如果uintmax_t恰好是128或256位,那么你当然可以这样做。我不知道任何处理器支持uintmax_t的128位或更高位,但该语言并不能阻止它。

    如果对“原子”的要求不如“需要确保100%肯定没有其他处理器同时更新”,那么使用常规SSE,AVX或AVX512指令就足够了 - 但是会有如果你有两个处理器(内核)同时对同一位内存进行读/修改/写操作,那就是竞争条件。

    x86上最大的原子操作是CMPXCHG16B,如果两个其他寄存器中的值与内存中的值匹配,它将把两个64位整数寄存器与内存中的内容交换。所以你可以想出一些东西,它读取一个128位值,然后输出一些内容,然后如果没有其他内容首先将原始值存回原来 - 如果发生这种情况,你必须重复操作,当然,它也不是单一的原子和操作。

    当然,在除Intel和AMD之外的其他平台上,行为可能会有所不同。

答案 1 :(得分:0)

如果内存读取/修改/写入都作为单个操作发生,则操作只能是原子操作。例如lock and [mem], %rax是原子的。 (英特尔的insn ref手册明确指出lock前缀确实与and一起使其成为原子。)

由于典型的AVX指令(如VPAND)可以具有内存源操作数(将内存读取与修改寄存器相结合),但内存目标操作数(读取/修改/写入),整个想法不会起作用。

Mats Petersson的回答很好地解释了你可以做什么,但我只是想指出为什么普通的AVX不能用作单指令原子操作。你必须加载,修改和cmpxchange,然后在读取load和cmpexchange之间修改内存时再试一次。