Multithreading 如果其他线程仅读取共享数据,是否需要OpenMP原子写入?

Multithreading 如果其他线程仅读取共享数据,是否需要OpenMP原子写入?,multithreading,openmp,shared-memory,atomic,atomicity,Multithreading,Openmp,Shared Memory,Atomic,Atomicity,我在C++中有一个OpenMP并行循环,其中所有线程访问双.y/p>的共享数组。 每个线程只在其自己的数组分区中写入。两个线程不能在同一数组项上写入 每个线程在其他线程编写的分区上读取。只要double是旧值或更新值(不是由于读取半写的double而导致的无效值),数据是否已由拥有分区的线程更新并不重要 我是否需要原子写入来确保读取的数据是有效的(旧的或更新的),还是仅当多个线程试图在同一位置写入时才需要原子写入 它似乎在使用和不使用原子写入的情况下都能工作,但当然,在没有原子写入的情况下速

我在C++中有一个OpenMP并行循环,其中所有线程访问双.y/p>的共享数组。
  • 每个线程只在其自己的数组分区中写入。两个线程不能在同一数组项上写入
  • 每个线程在其他线程编写的分区上读取。只要double是旧值或更新值(不是由于读取半写的double而导致的无效值),数据是否已由拥有分区的线程更新并不重要
我是否需要原子写入来确保读取的数据是有效的(旧的或更新的),还是仅当多个线程试图在同一位置写入时才需要原子写入


它似乎在使用和不使用原子写入的情况下都能工作,但当然,在没有原子写入的情况下速度更快。

我认为在您的情况下是安全的。我可以看到的一个问题是,writer线程在读取器读取它之前只完成了双精度的一半,从而导致一些损坏的值。例如(假设您使用的是64位整数),如果写入程序写入了-1的值,其中以前有一个零,但在读取器读取它之前只写入了前4个字节,那么读取器将读取40亿个字节,因为-1是两个组件表示中的所有1


实际上,就大多数现代建筑而言,我认为这是不可能的。如果您有64位cpu,那么对内存的读写应该以64位块的形式进行。这意味着上述腐败是不可能的。现在,如果您正在读取一个大型结构并将其写入内存,那么您需要研究同步。

您应该使用原子写入和读取来获得正确的可移植程序。按照以下规定:

2.13.6原子结构

[…]为了避免竞争条件,x指定的位置的所有访问 必须使用原子构造保护可能并行发生的事件

更详细地说:

1.4.1 OpenMP内存模型的结构

[……]

对一个变量的单一访问可以通过多个加载或存储指令来实现,因此不能保证相对于对同一变量的其他访问是原子的

[……]

如果至少有一个线程从内存单元读取数据,并且至少有一个线程在没有同步的情况下写入同一内存单元,包括上述由于原子性考虑而导致的情况,则会发生数据争用。如果发生数据竞争,则程序的结果未指定

除了原子性之外,还应该考虑能见度:

1.4.3冲洗操作

内存模型具有宽松的一致性,因为线程的内存临时视图不需要始终与内存一致。写入变量的值可以保留在线程的临时视图中,直到稍后强制将其存储。同样,从变量中读取可能会从线程的临时视图中检索值,除非强制从内存中读取。OpenMP刷新操作强制执行临时视图和内存之间的一致性

这意味着,除非您有任何显式或隐式内存刷新,否则无法保证您会看到更新的值

然而,原子版本决不一定慢。实现原子操作的编译器知道体系结构的特定内存模型,可以自由地利用它。事实上,gcc和clang都不支持在x86上以原子方式写入或读取
double
,而这样做是为了实现原子增量或
长double
操作。不幸的是,原子可能仍然会阻碍某些优化,但是如果省略
原子
,这些很可能会导致未指定的结果。不要低估编译器优化:对于严格来说不符合标准的明显合理的程序,很容易出现未定义的行为


至于内存刷新对性能的影响,您可以根据实际算法确定需要刷新内存的频率。

在我的情况下,使用原子读/写会使我的代码慢5倍。我在WIndows上使用icc16.0。我不明白为什么它这么慢,因为我在x64上使用了双值。在这种特殊情况下,读/写应该是原子的,对吗?如果该程序只打算在Windows x64上使用,我是否可以保证它在没有原子读/写的情况下能够成功工作?是的,icc确实会为原子操作生成特定的函数调用,即使是双精度操作,因此代码的效率要低得多。不幸的是,尽管您了解体系结构,但无法保证。例如,编译器可以自由地使用您违反的假设进行优化,从而导致未定义的行为。实际上,它可能会工作,我相信很多应用程序会忽略这些原子,并且运行良好。