3

我正在尝试通过 OpenMP 并行化一个 fortran 循环。循环本质上只包含两个命令:

do i=1,LSample
  calcSslice(Vpot(:,:,i), Sslice)
  rpold = rp
  combine_rp_matrices (rpold, Sslice, rp)
end do

calcSslice 子例程读取 Vpot(:,:,i),执行一些计算并将结果存储在矩阵 Sslice 中。combine_rp_matrices 使用 rpold 和 Sslice 来更新 rp。rp 充当运行变量,是程序的所需输出。来自不同迭代的 Sslice 矩阵与 rp 组合的顺序无关紧要。我第一次尝试并行化这个循环是这样的:

!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(Sslice), SCHEDULE(DYNAMIC)
do i=1,LSample
  calcSslice(Vpot(:,:,i), Sslice)
!$OMP CRITICAL
  rpold = rp
  combine_rp_matrices (rpold, Sslice, rp)
!$OMP END CRITICAL
end do
!$OMP END PARALLEL DO

这会编译并运行,但会产生错误的结果。使用以下代码,我得到正确的结果,但执行速度要慢得多(尽管仍然比序列化代码快):

!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(Sslice), SCHEDULE(DYNAMIC)
do i=1,LSample
!$OMP CRITICAL(Crit2)
  calcSslice(Vpot(:,:,i), Sslice)
!$OMP END CRITICAL(Crit2)
!$OMP CRITICAL
  rpold = rp
  combine_rp_matrices (rpold, Sslice, rp)
!$OMP END CRITICAL
end do
!$OMP END PARALLEL DO

因此,calcSslice 显然存在一些同步问题。但是,我不太明白这会发生在哪里。Vpot 只在 calcSslice 中读取而不写入,而 Sslice 是一个线程私有变量。calcSslice 中使用的任何全局变量也只能从中读取。变量 rpold 和 rp 在 DO 循环所属的子例程范围内声明,因此 calcSslice 无法访问。calcSslice 中声明的变量使用以下属性:intent(in)、intent(out)、target、pointer。

这哪里出错了?

编辑:问题已解决,原因是calcSslice声明期间变量的初始化,这意味着save属性。

4

1 回答 1

2

我的猜测是这calcSslice不是线程安全的。确保此子例程不访问只读以外的全局变量,并且不使用该save属性(如果在声明期间初始化变量,请注意隐式保存!)。您可以使用英特尔提供的线程检查器来查找代码中的竞争条件。如果您无法访问此类软件,我将从一个虚拟程序开始,然后逐步填充该例程以查看它失败的地方。

令我困惑的另一件事是循环体的最后两行。每个线程都备份整个矩阵,然后添加他的切片。reduction收集所有切片(例如通过一个子句)然后将那个大切片组合一次不是更好吗?

于 2013-09-08T05:53:16.427 回答