我在 C++ 中有一个 openmp 并行循环,其中所有线程都访问一个共享的 double 数组。
- 每个线程仅在其自己的数组分区中写入。两个线程不能写入同一个数组条目。
- 每个线程读取其他线程写入的分区。只要双精度值是旧值或更新值(不是读取半写入的双精度值所导致的无效值),数据是否已被拥有该分区的线程更新并不重要。
我是否需要原子写入来确保读取的数据有效(旧的或更新的),或者仅当多个线程尝试在同一位置写入时才需要原子写入?
它似乎可以在有或没有原子写入的情况下工作,但如果没有原子写入,当然速度更快。
你应该使用原子写入和读取以获得正确的可移植程序。指定为标准 http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf:
2.13.6 原子构造
[...]为了避免竞争条件,对 x 指定的位置的所有访问都可以
可能并行发生的情况必须使用原子构造进行保护。
更详细地说:
1.4.1 OpenMP 内存模型的结构
[...]
对变量的单次访问可以使用多个加载或存储指令来实现,因此不能保证相对于对同一变量的其他访问是原子的。
[...]
如果至少一个线程从存储器单元读取并且至少一个线程在没有同步到该同一存储器单元的情况下写入,包括由于如上所述的原子性考虑的情况,则发生数据争用。如果发生数据竞争,则程序的结果是不确定的。
除了原子性之外,还应该考虑可见性:
1.4.3 冲洗操作
内存模型具有宽松的一致性,因为线程的临时内存视图不需要始终与内存保持一致。写入变量的值可以保留在线程的临时视图中,直到稍后将其强制存储到内存中。同样,从变量读取可以从线程的临时视图中检索值,除非强制从内存中读取。 OpenMP 刷新操作强制临时视图和内存之间的一致性。
这意味着,除非您有任何显式或隐式内存刷新,否则无法保证您会看到更新的值。
然而,原子版本并不一定较慢。实现原子操作的编译器知道该体系结构的特定内存模型并可以自由地利用它。事实上 gcc 和 clang 都不是生成昂贵的锁 https://godbolt.org/g/2lFnXs用于原子地写入或读取double
在 x86 上,同时这样做是为了原子增量或long double
运营。不幸的是,原子可能仍然会阻碍某些优化 - 但如果您省略了这些优化,这些很可能会导致未指定的结果atomic
。不要低估编译器优化:对于严格来说不符合标准的明显合理的程序,很容易出现未定义的行为。
至于内存刷新对性能的影响,取决于您的实际算法需要刷新内存的频率。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)