使用OpenMp在内部循环中循环数组

时间:2016-02-29 14:25:58

标签: fortran openmp

该片段是我想要完成的通用版本。

program main
integer, save:: j,Nj,k,Nk,AllocateStatus
!double precision:: array(100000)
double precision, dimension(:), allocatable:: array

INTEGER NTHREADS, TID, OMP_GET_NUM_THREADS,
 +        OMP_GET_THREAD_NUM

Nj = 100000
Nk = 100
allocate(array(Nk),STAT = AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

array = 0.0
!$OMP PARALLEL PRIVATE(NTHREADS, TID)
!$OMP DO
do j=1, Nj

!print *, "id", OMP_GET_THREAD_NUM()
!DO COMPUTATIONALLY INTENSIVE PART


    do k=1, Nk
        array(k)=array(k)+1
    enddo

enddo
!$OMP END DO
!$OMP END PARALLEL

print *, array
stop
end

在非openmp版本中,array的每个元素都是100000.使用代码段中使用的openmp,我得到大约99000的数组元素。我不清楚我需要做什么改变make使openmp版本产生与串行版本相同的输出。

编辑:

外部循环中完成的操作彼此之间没有依赖关系,但这些操作的输出需要在array之类的变量中进行累积跟踪。因此,每个线程的临时数组可以在外循环完成后组合起来,但是我不知道如何进行减少。下面的代码是否有意义?

program main
integer, save:: j,Nj,k,Nk,AllocateStatus
!double precision:: array(100000)
double precision, dimension(:), allocatable:: a_tmp,a_total

INTEGER NTHREADS, TID, OMP_GET_NUM_THREADS,
 +        OMP_GET_THREAD_NUM

Nj = 100000
Nk = 100
allocate(a_tmp(Nk),STAT = AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

    allocate(a_total(Nk),STAT = AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"

a_tmp = 0.0
a_total = 0.0
!$OMP PARALLEL PRIVATE(NTHREADS, TID,a_tmp)
!$OMP DO
do j=1, Nj

!print *, "id", OMP_GET_THREAD_NUM()

    do k=1, Nk
        a_tmp(k)=a_tmp(k)+1
    enddo

enddo
!$OMP END DO
a_total=a_total+a_tmp
!$OMP END PARALLEL

print *, a_total
stop
end

1 个答案:

答案 0 :(得分:2)

这是一个非常通用的答案。

你的循环很不稳定。正如您所写的那样,j的值将在线程之间平均分配,因此线程1获得j=1..Nj/num_threads,线程2获得j=(Nj/num_threads)+1..2*Nj/num_threads,依此类推。但是每个线程都会执行

do k=1, Nk

表示所有值1..Nk。这意味着所有线程都将更新array(k),并且您无法控制何时或如何发生。特别是,你不能阻止线程1读取一个值,然后线程2读取相同的值,然后向它添加1并将其写回,然后线程1为同一元素写回自己的值 - 从而丢失一次更新变量值。

您已编写数据竞赛。没有什么可以羞耻的,我们都做到了。

你如何删除这是另一个问题,它取决于你想要实现的目标。例如,您可以在k而不是j上进行并行化。但是,您向我们展示的简单代码可能过于简单。我要说在OpenMP中编写嵌套循环是相对不寻常的,其中内部循环的内容在某种程度上不包含外部循环迭代器(在您的情况下为j)的值,因此他们没有实施数据竞赛。

例如

!$OMP DO
    do j=1, Nj
        do k=1, Nk
            array(j)=array(j)+1
        enddo
    enddo

没有数据竞争;编译器/运行时将负责分配循环迭代,并且不会发生多次写入。另一方面,它表达的操作与您编程的操作不同。

另一种方法是使用OpenMP collapse指令,它将融合两个循环并负责消除数据竞争。您的教程材料或其他Q和As上的SO将向您展示如何使用它。