我有一个将粒子分布到云端网格中的程序。简单地循环遍历粒子总数(Ntot)并填充256 ^ 3网格(即每个粒子分布在8个单元格上)。
% gfortran -fopenmp cic.f90 -o ./cic
编译好。但是当我运行它(./cic)时,我会遇到分段错误。我的循环是一个经典的omp do问题。当我不在openmp中编译它时,该程序有效。
!$omp parallel do
do i = 1,Ntot
if (x1(i).gt.0.and.y1(i).gt.0.and.z1(i).gt.0) then
dense(int(x1(i)),int(y1(i)),int(z1(i))) = dense(int(x1(i)),int(y1(i)),int(z1(i))) &
+ dx1(i) * dy1(i) * dz1(i) * mpart
end if
if (x2(i).le.Ng.and.y1(i).gt.0.and.z1(i).gt.0) then
dense(int(x2(i)),int(y1(i)),int(z1(i))) = dense(int(x2(i)),int(y1(i)),int(z1(i))) &
+ dx2(i) * dy1(i) * dz1(i) * mpart
end if
if (x1(i).gt.0.and.y2(i).le.Ng.and.z1(i).gt.0) then
dense(int(x1(i)),int(y2(i)),int(z1(i))) = dense(int(x1(i)),int(y2(i)),int(z1(i))) &
+ dx1(i) * dy2(i) * dz1(i) * mpart
end if
if (x2(i).le.Ng.and.y2(i).le.Ng.and.z1(i).gt.0) then
dense(int(x2(i)),int(y2(i)),int(z1(i))) = dense(int(x2(i)),int(y2(i)),int(z1(i))) &
+ dx2(i) * dy2(i) * dz1(i) * mpart
end if
if (x1(i).gt.0.and.y1(i).gt.0.and.z2(i).le.Ng) then
dense(int(x1(i)),int(y1(i)),int(z2(i))) = dense(int(x1(i)),int(y1(i)),int(z2(i))) &
+ dx1(i) * dy1(i) * dz2(i) * mpart
end if
if (x2(i).le.Ng.and.y1(i).gt.0.and.z2(i).le.Ng) then
dense(int(x2(i)),int(y1(i)),int(z2(i))) = dense(int(x2(i)),int(y1(i)),int(z2(i))) &
+ dx2(i) * dy1(i) * dz2(i) * mpart
end if
if (x1(i).gt.0.and.y2(i).le.Ng.and.z2(i).le.Ng) then
dense(int(x1(i)),int(y2(i)),int(z2(i))) = dense(int(x1(i)),int(y2(i)),int(z2(i))) &
+ dx1(i) * dy2(i) * dz2(i) * mpart
end if
if (x2(i).le.Ng.and.y2(i).le.Ng.and.z2(i).le.Ng) then
dense(int(x2(i)),int(y2(i)),int(z2(i))) = dense(int(x2(i)),int(y2(i)),int(z2(i))) &
+ dx2(i) * dy2(i) * dz2(i) * mpart
end if
end do
!$omp end parallel do
迭代之间没有依赖关系。想法?
答案 0 :(得分:2)
此问题以及your other question中的问题来自于启用OpenMP时禁用自动堆阵列的问题。这意味着没有-fopenmp
,大数组会自动放入静态存储(称为.bss
段),而小数组则在堆栈上分配。当您打开OpenMP支持时,不会使用自动静态分配,并且您的dense
数组将在例程的堆栈上分配。 OS X上的默认堆栈限制非常严格,因此会出现分段错误。
这里有几个选项。第一个选项是通过为dense
属性赋予SAVE
静态分配。另一种选择是通过使它ALLOCATABLE
然后使用ALLOCATE
语句在堆上显式分配它,例如:
REAL, DIMENSION(:,:,:), ALLOCATABLE :: dense
ALLOCATE(dense(256,256,256))
! Computations, computations, computations
DEALLOCATE(dense)
较新的Fortran版本支持在超出范围时自动释放没有SAVE
属性的数组。
请注意,您的OpenMP指令很好,不需要额外的数据共享子句。您不需要在i
子句中声明PRIVATE
,因为循环计数器具有预定的私有数据共享类。您不需要将其他变量放在SHARED
子句中,因为它们是隐式共享的。但是,您在dense
上执行的操作应该与ATOMIC UPDATE
同步(或者在较早的OpenMP实现上只是ATOMIC
),或者您应该使用REDUCTION(+:dense)
。原子更新被转换为锁定的添加,并且不应该因为在循环中有条件的巨大减速而导致减速:
INTEGER :: xi, yi, zi
!$OMP PARALLEL DO PRIVATE(xi,yi,zi)
...
if (x1(i).gt.0.and.y1(i).gt.0.and.z1(i).gt.0) then
xi = int(x1(i))
yi = int(y1(i))
zi = int(z1(i))
!$OMP ATOMIC UPDATE
dense(xi,yi,zi) = dense(xi,yi,zi) &
+ dx1(i) * dy1(i) * dz1(i) * mpart
end if
...
使用适当的更改为其他情况复制代码。如果您的编译器抱怨UPDATE
构造中的ATOMIC
子句,只需删除它。
REDUCTION(+:dense)
会在每个线程中创建dense
的一个副本,这会消耗大量内存,最终应用的减少会随着dense
的大小而变得越来越慢。对于小型数组,它比原子更新效果更好。
答案 1 :(得分:-1)
有关如何使变量共享和私有的说明,请参阅https://computing.llnl.gov/tutorials/openMP/#Clauses。
除了必须是私有的循环变量i
之外,看起来应该共享所有变量。这将建议使用以下行:
!$omp parallel do default(shared) private(i)
这应该可以解决你的分段错误(假设我得到了所有变量
)但是,存在不同线程同时尝试覆盖dense
的相同部分的风险,从而导致总计不正确。为了防止出现这种情况,您需要将dense
或!$omp atomic
部分中的每个作业包装到!$omp critical
。
但是,您可能会发现这些关键部分会导致线程花费大部分时间等待,因此您可能看不到对纯串行代码的任何改进。
原则上,您可以通过使用dense
关键字声明reduction
来解决此问题,但不幸的是,它不能用于数组。