【问题标题】:OpenMP atomic and non-atomic reads/writes produce the same instructions on x86_64OpenMP 原子和非原子读/写在 x86_64 上产生相同的指令
【发布时间】:2016-02-17 16:32:43
【问题描述】:

根据 OpenMP 规范 (v4.0),由于i 的不同步读/写,以下程序包含可能的数据竞争:

int i{0}; // std::atomic<int> i{0};

void write() {
// #pragma omp atomic write // seq_cst
   i = 1;
}

int read() {
   int j;
// #pragma omp atomic read // seq_cst
   j = i; 
   return j;
}

int main() {
   #pragma omp parallel
   { /* code that calls both write() and read() */ }
}

我想到的可能解决方案在代码中显示为 cmets:

  1. #pragma omp atomic write/read保护i的读写,
  2. #pragma omp atomic write/read seq_cst保护i的读写,
  3. 使用std::atomic&lt;int&gt; 而不是int 作为i 的类型。

以下是 x86_64 上编译器生成的指令(在所有情况下都使用 -O2):

GNU g++ 4.9.2:               i = 1;        j = i;
original code:               MOV           MOV
#pragma omp atomic:          MOV           MOV
// #pragma omp atomic seq_cst:  MOV           MOV
#pragma omp atomic seq_cst:  MOV+MFENCE    MOV    (see UPDATE)
std::atomic<int>:            MOV+MFENCE    MOV

clang++ 3.5.0:               i = 1;        j = i;
original code:               MOV           MOV
#pragma omp atomic:          MOV           MOV
#pragma omp atomic seq_cst:  MOV           MOV
std::atomic<int>:            XCHG          MOV

Intel icpc 16.0.1:           i = 1;        j = i;
original code:               MOV           MOV
#pragma omp atomic:          *             *
#pragma omp atomic seq_cst:  *             *
std::atomic<int>:            XCHG          MOV

* Multiple instructions with calls to __kmpc_atomic_xxx functions.

我想知道为什么 GNU/clang 编译器不会为 #pragma omp atomic 写入生成任何特殊指令。我希望有与std::atomic 类似的说明,即MOV+MFENCEXCHG。有什么解释吗?

更新

g++ 5.3.0 为#pragma omp atomic write seq_cst 生成MFENCE。这是正确的行为,我相信。没有seq_cst,它会产生普通的MOV,这对于非SC原子性来说已经足够了。

我的 Makefile 中有一个错误,g++ 4.9.2 也会为 CS 原子写入生成 MFENCE。对不起,伙计们。

Clang 3.5.0 没有实现 OpenMP SC 原子,感谢 Hristo Iliev 指出这一点。

【问题讨论】:

  • 我的 GCC 4.9.2 在movl $1, i(%rip) 之后立即生成一个mfence,用于顺序一致的原子写入。
  • 另外,Clang 3.5.0 仅支持常规的非顺序一致的原子。它甚至没有完整的 OpenMP 3.1 支持 - 请参阅 here
  • 您的 GCC 4.9.2 为 OpenMP SC 原子写入生成 mfence?也就是说,i 的类型是 int?我的 GCC 仅适用于 std::atomic&lt;int&gt;
  • 我刚刚意识到 g++ 5.3.0 为 SC OpenMP 原子写入生成 mfence。所以,问题出在(我的)g++ 4.9.2 上。
  • 我想知道你的 4.9.2 和我的 4.9.2 有什么不同。我怀疑 GCC 中的机器规格可能会有所不同。您使用的是什么操作系统和发行版?

标签: c++ openmp x86-64 atomic memory-fences


【解决方案1】:

有两种可能。

  1. 编译器没有义务将包含数据竞争的 C++ 代码转换为错误的机器代码。根据机器内存模型,通常使用的指令可能已经是原子的和连贯的。将相同的 C++ 代码带到另一个架构中,您可能会开始看到编译指示导致 x86_64 上不存在的差异。

  2. 除了可能导致使用不同的指令和/或额外的内存栅栏指令外,原子编译指示(以及std::atomicvolatile)还限制了编译器自己的代码重新排序优化。它们可能不适用于您的简单情况,但您肯定会看到公共子表达式消除(包括在循环外提升计算)可能会受到影响。

【讨论】:

  • 我同意,但是,单独使用 MOV 不足以实现顺序一致的原子存储(例如,请参阅 hereHerb Sutter's lecture around 0:35:00)。因此,我希望XCHGMFENCE 对应#pragma omp atomic write seq_cst
  • 但是,假设数据正确对齐,mov 对于简单的原子(没有 seq_cst)就足够了,因为在 X86 中不会发生撕裂。 (构成该值的所有字节都由 mov 以原子方式写入)。如果没有 seq_cst,原子结构并不意味着 OpenMP“刷新”。
  • @Jim Cownie:你确定同花顺吗?来自 OpenMP 规范。 4.0:刷新区域带有列表在以下位置隐含:在非顺序一致的原子区域中执行的原子操作的进入和退出 ,其中列表仅包含根据第 127 页第 2.12.6 节中原子构造的语法描述指定为 x 的存储位置。 并且:刷新区域 没有列表 隐含在以下位置: ... 在进入和退出在顺序一致的原子区域中执行的原子操作时。
  • @Daniel:请参阅 4.5 规范,其中说:“任何带有 seq_cst 子句的原子构造都会强制原子执行的操作为 17,包括不带列表的隐式刷新操作。”由于它提出了这种情况,我假设这也意味着没有 seq_cst 的构造并不意味着刷新。它还明确(尽管在非规范性文本中)说“非顺序一致的原子构造与 C++11/C11 中的 memory_order_relaxed 原子操作具有相同的语义”。 (而且,你引用的文本只需要刷新有问题的变量,这是一个简单的 mov 。)
  • @Jim Cownie:我引用的文本在 4.5 规范中。同样,因此即使是非 SC 原子也意味着刷新,尽管与列表刷新。你是对的,mov 提供了这个。对于 SC 原子,我希望有额外的栅栏来防止在 CPU 级别重新排序,但在我的情况下,它不是由 g++ 放入程序中的。这就是我想知道的;对于 OpenMP 和 C++11 内存模型来说,顺序一致性意味着什么不同吗?
猜你喜欢
  • 2021-04-01
  • 2014-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多