我正在尝试通过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中读取,而不能写入calcSslice中,并且Sslice是threadprivate变量。 calcSslice中使用的所有全局变量也只能从中读取。变量rpold和rp在DO循环所属的子例程的范围内声明,因此calcSslice无法访问该变量。在calcSslice中声明的变量使用以下属性:intent(in),intent(out),target,pointer。
这哪里出错了?
编辑:问题已解决,原因是声明期间
calcSslice
中变量的初始化,这意味着save
属性。 最佳答案
我的猜测是calcSslice
不是线程安全的。确保此子例程不访问只读以外的全局变量,并且不使用save
属性(如果在声明期间初始化变量,请注意隐式保存!)。
您可以使用英特尔提供的那种线程检查器来在代码中查找竞争条件。如果您无权使用此类软件,我将以虚拟过程开始,然后以增量方式填充例程以查看其失败之处。
令我困惑的另一件事是循环主体的最后两行。每个线程都会备份整个矩阵,然后添加其 slice 。收集所有 slice (例如通过reduction
子句)然后将大 slice 合并一次会更好吗?