我想尝试使用这样的东西原子重置 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指令都可以原子地执行获取和吗?
所以有几件不同的事情需要解决:
- 处理器能做什么?
- 原子是什么意思?
- 你能让编译器为处理器可以做的事情生成代码吗?
- C++11/14 标准是否支持这一点?
对于 #1 和 #2:
在 x86 中,有执行 8、16、32、64、128、256 和 512 位操作的说明。一个处理器将[至少如果数据与其自身的大小对齐]以原子方式执行该操作。但是,要使操作成为"真正的原子",它还需要防止该数据更新中的争用条件[换句话说,防止其他处理器读取,修改和写回同一位置]。除了少量的"隐含锁定"指令外,这是通过向特定指令添加"锁定前缀"来完成的 - 这将对系统中的其他处理器执行正确的缓存对话[技术术语],以确保只有此处理器可以更新此数据。
我们不能使用带有 LOCK 前缀的 VEX 指令(来自英特尔手册)
任何在 VEX 前面带有 LOCK 前缀的 VEX 编码指令都将 #UD
您需要一个 VEX 前缀才能使用 AVX 指令,#UD 表示"未定义的指令"——换句话说,如果我们尝试执行它,代码将导致处理器异常。
因此,可以100%确定处理器不能一次对256位进行原子操作。这个答案讨论了 SSE 指令原子性:SSE 指令:哪些 CPU 可以执行原子 16B 内存操作?
如果指令无效,#3 将毫无意义。
#4 - 好吧,标准支持 std::atomic<uintmax_t>
,如果uintmax_t
恰好是 128 或 256 位,那么您当然可以这样做。我不知道有任何处理器支持 128 位或更高的位 uintmax_t
,但语言并没有阻止它。
如果对"原子"的要求不如"需要确保100%肯定没有其他处理器同时更新"那么强烈,那么使用常规的SSE,AVX或AVX512指令就足够了 - 但是如果你有两个处理器(内核)同时在同一位内存上执行读/修改/写操作,就会有竞争条件。
x86 上最大的原子操作是 CMPXCHG16B,如果另外两个寄存器中的值与内存中的值匹配,它将用内存中的内容交换两个 64 位整数寄存器。所以你可以想出一些东西来读取一个 128 位值,并输出一些位,然后原子地存储新值,如果没有其他东西先进入那里 - 如果发生这种情况,你必须重复操作,当然,它也不是一个单一的原子和操作。
当然,在英特尔和AMD以外的其他平台上,行为可能会有所不同。
仅当内存读取/修改/写入全部作为单个操作发生时,该操作才能是原子操作。 例如 lock and [mem], %rax
是原子的。 (英特尔的 insn 参考手册明确指出,lock
前缀确实适用于and
以使其原子化。
由于像VPAND
这样的典型AVX指令可以有内存源操作数(将内存读取与修改寄存器相结合),但没有内存目标操作数(读/修改/写),所以整个想法是行不通的。
Mats Petersson的回答很好地解释了你可以做什么,但我只是想指出为什么普通的AVX不可能用作单指令原子操作。 您必须加载、修改和 cmpxchange,如果读取加载和 cmpexchange 之间的内存有其他内容修改,请重试。