c++ - 可以原子地获取和运算的最大数据类型?

标签 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 位类型太大而无法保证原子性吗?

还有其他方法可以实现吗?我正在使用海湾合作委员会。

如果不是,我可以自动重置的最大类型是什么 - 64 位?

编辑:任何 AVX 指令都可以原子地执行取指与操作吗?

最佳答案

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

  1. 处理器可以做什么?
  2. 我们所说的原子是什么意思?
  3. 你能让编译器为处理器的功能生成代码吗?
  4. C++11/14 标准支持吗?

对于#1 和#2:

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

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

Any VEX-encoded instruction with a LOCK prefix preceding VEX will #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 以外的其他平台上,行为可能会有所不同。

关于c++ - 可以原子地获取和运算的最大数据类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30948832/

相关文章:

c++ - DXGI 桌面复制屏幕捕获速度

c++ - 如何在 C++ 中对 vector 组元素进行排序?

vector - 不允许 C++ 命名空间类型名称

c++ - 正确使用 volatile 与 std::atomic_ref<T>

assembly - 在代码执行时将未对齐写入机器代码中的立即操作数是否安全?

c++ - 如何在不使用 OLE DB API 的情况下使用 C++ 查询 MS SQL Compact Server 3.5 数据库?

c++ - 如何在 catch 语句之外使用异常

c++ - Qt blockSignals 不会阻止第一次额外点击

C++ Vector.erase() 导致段错误

Objective-C:标量属性默认为原子?