如果这不是竞争条件,请原谅我;我对命名法并不熟悉。
我遇到的问题是,在启用OpenMP的情况下,此代码运行速度较慢。我认为循环应该足够大(k = 100,000),所以我认为开销不是问题。
据我了解,此处出现竞争条件,因为所有循环都试图始终访问相同的v(i,j)值,从而减慢代码速度。
这里最好的解决方法是创建尽可能多的v()数组副本作为线程并让每个线程访问另一个吗?
我在16核上使用intel编译器,运行速度比单核更慢。 谢谢大家!
!$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
答案 0 :(得分:3)
你当然已经编制了一个竞争条件,但我不确定这是否是导致你的程序无法更快执行的原因。这一行
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
在每次迭代中看起来你都在为你的程序绑定一个锚,为什么不只是
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
中由另一个线程更新(在您无法控制的时间)的相邻元素。要解决这个问题......必须重新开始工作。