如果这实际上不是竞争条件,请原谅我;我不太熟悉这些术语。
我遇到的问题是此代码在启用 OpenMP 的情况下运行速度较慢。我认为循环应该足够大 (k=100,000),所以我认为开销不是问题。
据我所知,这里出现了竞争条件,因为所有循环都试图一直访问相同的 v(i,j) 值,从而减慢了代码速度。
这里最好的解决方法是创建与线程一样多的 v() 数组副本,并让每个线程访问不同的线程吗?
我在 16 核上使用英特尔编译器,它的运行速度比在单核上稍慢。 谢谢大家!
!$OMP PARALLEL DO
Do 500, k=1,n
Do 10, i=-(b-1),b-1
Do 20, j=-(b-1),b-1
if (abs(i).le.l.and.abs(j).eq.d) then
cycle
endif
v(i,j)=.25*(v(i+1,j)+v(i-1,j)+v(i,j+1)+v(i,j-1))
if (k.eq.n-1) then
vtest(i,j,1)=v(i,j)
endif
if (k.eq.n) then
vtest(i,j,2)=v(i,j)
endif
20 continue
10 continue
500 continue
!$OMP END PARALLEL DO
最佳答案
您肯定编写了竞争条件,但我不确定这是导致您的程序无法更快执行的原因。这条线
v(i,j)=.25*(v(i+1,j)+v(i-1,j)+v(i,j+1)+v(i,j-1))
将由所有线程为 i
和 j
的相同(一组)值执行,这就是竞速发生的地方。鉴于您的程序没有做任何事情来协调对 v
元素的读取和写入,您的程序在实践中是不确定的,因为无法知道 v< 的更新顺序
制作。
您应该在检查程序结果时观察到这种不确定性,并且注意到更改线程数也会对结果产生影响。话又说回来,通过对数组进行长时间运行的模板操作,结果可能会收敛到相同(或足够相似)的值。
OpenMP 为您提供了协调变量访问的工具,但它不会自动实现它们;幕后肯定没有任何事情可以防止对 v
的准同步读取和写入。因此,缺乏性能改进的解释在别处。这可能归因于多线程对系统内存层次结构中某个级别缓存的影响。一个很好的、缓存友好的、按内存顺序遍历数组的每个元素的串行程序变成了一场暴风雪(就缓存而言)随机访问内存,每次都需要访问 RAM。
解释可能在别处。如果执行 OpenMP 版本的时间比执行串行版本的时间稍长,我怀疑该程序实际上并未并行执行。未能正确编译是一个常见的原因(此处为 SO)。
如何解决这个问题?
OpenMP 跨数组的常用模式是在其中一个数组索引上并行化。声明
!$omp parallel do
do i=-(b-1),b-1
....
end do
确保每个线程为 i
获取一组不同的值,这意味着它们写入 v
的不同元素,消除(几乎)数据竞争。在您编写程序时,每个线程都获得一组不同的 k
值,但在内部循环中没有(太多)使用。
顺便说一下,测试
if (k==n-1) then
和
if (k==n) then
在每次迭代中,您似乎都在为您的程序绑定(bind)一个 anchor ,为什么不呢
do k=1,n-2
并在循环结束时处理对 vtest
的更新。
你可以像这样分开 !$omp parallel do
!$omp parallel
do k=1,n-2
!$omp do
do i=-(b-1),b-1
(并在并行循环和区域的末尾进行相应的更改)。现在所有线程都执行并行区域的全部内容,但每个线程都有自己的一组 i
值来使用。我建议您在指令中添加子句以指定每个变量的可访问性(例如 private
或 shared
);但是这个答案有点太长了,我不会详细介绍这些。或者使用 schedule
子句。
当然,最后,即使有我建议的更改,你的程序也将是不确定的,因为这个声明
v(i,j)=.25*(v(i+1,j)+v(i-1,j)+v(i,j+1)+v(i,j-1))
将从 v
读取相邻元素,这些元素由另一个线程更新(在您无法控制的时候)。要解决这个问题……必须回去工作。
关于parallel-processing - 如何删除 Fortran 竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20413625/