我有一个相对简单的循环,使用暴力方法计算粒子系统的净加速度。
我拥有一个工作正常的OpenMP循环,它循环遍历每个粒子,并将其与其他每个粒子进行比较,具有n^2的复杂度:
!$omp parallel do private(i) shared(bodyArray, n) default(none)
do i = 1, n
!acc is real(real64), dimension(3)
bodyArray(i)%acc = bodyArray(i)%calcNetAcc(i, bodyArray)
end do
这个程序已经可以很好地工作。
现在我想通过仅计算每个物体上的力一次来减少计算时间,利用了F(a->b) = -F(b->a)的事实,将需要计算的相互作用数量减半(n^2 / 2)。我在以下循环中实现了此操作:
call clearAcceleration(bodyArray) !zero out acceleration
!$omp parallel do private(i, j) shared(bodyArray, n) default(none)
do i = 1, n
do j = i, n
if ( i /= j .and. j > i) then
bodyArray(i)%acc = bodyArray(i)%acc + bodyArray(i)%accTo(bodyArray(j))
bodyArray(j)%acc = bodyArray(j)%acc - bodyArray(i)%acc
end if
end do
end do
但我在并行化这个循环时遇到了很多困难,一直得到垃圾结果。我认为问题出在这一行代码:
bodyArray(j)%acc = bodyArray(j)%acc - bodyArray(i)%acc
并且各种 'j' 的写入没有正确地加起来。我尝试使用原子语句,但是数组变量不允许这样做。然后我尝试了关键部分,但这增加了大约20倍的时间,仍然没有给出正确的结果。我还尝试添加有序语句,但是结果都是 NaN。有没有简单的方法可以使用OpenMP使此循环正常工作?
以下是代码,它略微提高了速度,但不是我想要的 ~2 倍速度。
!$omp parallel do private(i, j) shared(bodyArray, forces, n) default(none) schedule(guided)
do i = 1, n
do j = 1, i-1
forces(j, i)%vec = bodyArray(i)%accTo(bodyArray(j))
forces(i, j)%vec = -forces(j, i)%vec
end do
end do
!$omp parallel do private(i, j) shared(bodyArray, n, forces) schedule(static)
do i = 1, n
do j = 1, n
bodyArray(i)%acc = bodyArray(i)%acc + forces(j, i)%vec
end do
end do