Fortran OpenMP计算部分和

时间:2019-03-27 22:47:23

标签: fortran openmp reduction

我对Fortran还是很陌生,并且正在开展一项涉及通过OpenMP并行求和数字的练习。

我被告知以下代码正确地计算了通过OpenMP并行计算的数字之和

graph->edge.size()

如果我调整了上面的代码以便可以在自己的Fortran程序中运行它,则会产生

!$omp parallel do private (I)
!$omp+ reduction(+:totals)
do I=1,100
    totals = totals + localsum(I)
enddo
!$omp end parallel do

该程序返回

Program test
    implicit none
    real totals
    double precision, dimension (1 : 100) :: localsum
    integer I

    !$omp parallel do private (I)
    !$omp+ reduction(+:totals)
    do I=1,100
        localsum(I)=I
        totals = totals + localsum(I)
    enddo
    !$omp end parallel do
    print *, 'The calculated sum total is', totals
end

但是,我不确定为什么需要为此添加额外的行

The calculated sum total is   5050.00000

当原始代码没有此行时。我注意到,如果我删除

localsum(I)=I

然后

!$omp+ reduction(+:totals)

返回

Program test
    implicit none
    real totals
    double precision, dimension (1 : 100) :: localsum
    integer I

    !$omp parallel do private (I)
    do I=1,100
        localsum(I)=I
        totals = totals + localsum(I)
    enddo
    !$omp end parallel do
    print *, 'The calculated sum total is', totals
end

当计算的总数应该是错误的。为了计算正确的总数,必须包括减少量 The calculated sum total is 5050.00000

是否有另一种方法来调整do循环以匹配提供的原始代码?我不确定为什么必须更改

!$omp+ reduction(+:totals)

do I=1,100
    totals = totals + localsum(I)
enddo

为了计算局部和。

2 个答案:

答案 0 :(得分:3)

这是Odd results with !$omp reduction in Fortran OpenMP的副本。

根据OpenMP规范(p。42),OpenMP伪指令的正确延续是在上一行的末尾使用MediaProjectionManager.createScreenCaptureIntent();,在延续行使用&!$omp&是可选的)。因此,您的代码应如下所示:

!$omp&

由于编译器忽略了定义Program test implicit none real totals integer I integer, dimension(100) :: localsum !$omp parallel do private (I) & !$omp& reduction(+:totals) do I=1,100 localsum(I)=I totals = totals + localsum(I) enddo !$omp end parallel do print *, 'The calculated sum total is', totals end 变量归约的续行,因此您得到了任意结果。添加正确的延续后,我得到正确的结果:

totals

答案 1 :(得分:0)

有无!$omp+ reduction(+:totals),执行的代码是不同的。

没有此指令,您将直接更新全局变量totals。这可能有效(在您的示例中有效),但远不能保证。问题在于这可能导致比赛。

假设线程a和线程b要更新此变量。他们需要:
1.从内存中获取var
2.在处理器中进行更新
3.将其写回内存

线程a和b中这些操作的相对顺序是什么?未指定。
如果顺序为1a2a3a1b2b3b,则没有问题。
如果它是1a1b2a2b3a3b,则会出现问题:1a1b(线程a和b取相同的值)2a2b(它们或多或少地同时更新它)3a3b(线程a写入结果并被线程b值覆盖)。

为避免这种情况,您可以使用原子操作,以确保不会中断读取-修改-写入周期,但是这非常昂贵,并且可能会大大降低执行时间。

为避免这种情况,必须使用减少量。第!$omp+ reduction(+:totals)行告诉openmp以安全有效的方式进行还原。实际要做的是

  1. 设置一个隐藏的局部变量以在部分循环中进行累加
  2. 在循环的每次迭代中,都会在此本地变量中进行累加
  3. 最后,以安全的方式将这些部分结果累积到全局变量totals中:原子操作将以正确更新全局变量的方式执行,并避免线程之间的竞争。

仍然有原子更新,但数量减少了,并且累积主要通过快速的本地操作来完成。

关于行localsum(I)=I的有用性,要求向量localsum事先未初始化。但是,如果目标只是添加第一个整数,则可以使用

do I=1,100
    totals = totals + I
enddo

性能将得到改善,结果是相同的。而且两个循环都以类似的方式并行化。