openMP没有改善运行时

时间:2014-08-07 18:04:24

标签: fortran openmp

我继承了一段Fortran代码,因为它的任务是为我们拥有的8核机器并行化。我有两个版本的代码,我正在尝试使用openMP编译器指令来加速它。它适用于一段代码,但不适用于其他代码,我无法弄清楚为什么 - 它们几乎相同! 我使用和不使用openMP标记运行每段代码,第一段显示速度提升,但不是第二块。我希望我能清楚地解释这个......

代码示例1 :(显着改进)

    !$OMP PARALLEL DO
    DO IN2=1,NN(2)
        DO IN1=1,NN(1)
            SCATT(IN1,IN2) = DATA((IN2-1)*NN(1)+IN1)/(NN(1)*NN(2))
            UADI(IN1,IN2) = SCATT(IN1,IN2)+1.0
        ENDDO
    ENDDO
    !$OMP END PARALLEL DO

代码示例2 :(没有改进)

    !$OMP PARALLEL DO
    DO IN2=1,NN(2)
        DO IN1=1,NN(1)
            SCATTREL = DATA(2*((IN2-1)*NN(1)+IN1)-1))/NN(1)*NN(2))
            SCATTIMG = DATA(2*((IN2-1)*NN(1)+IN1)))/NN(1)*NN(2))
            SCATT(IN1,IN2) = DCOMPLX(SCATREL, SCATIMG)
            UADI(IN1,IN2) = SCATT(IN1,IN2)+1.0
        ENDDO
    ENDDO        
    !$OMP END PARALLEL DO

我认为这可能是内存ovehead等问题,并尝试过将变量置于shared()和private()子句中的各种组合,但它们会导致分段错误或使其更慢。

我还认为可能是因为我在循环中没有做足够的工作来看到改进,但是因为对我来说没有意义的小循环有所改进。

任何人都可以了解我能做些什么看到第二个真正的速度提升?

代码示例1的速度提升数据:

平均运行时间(对于整个代码而不仅仅是此代码段)

Without openMP tags: 2m 21.321s 

With openMP tags: 2m 20.640s

平均运行时间(仅此代码段的配置文件)

Without openMP tags: 6.3s

With openMP tags: 4.75s

代码示例2的速度提升数据:

平均运行时间(对于整个代码而不仅仅是此代码段)

Without openMP tags: 4m 46.659s

With openMP tags: 4m 49.200s

平均运行时间(仅此代码段的配置文件)

Without openMP tags: 15.14s

With openMP tags: 46.63s

1 个答案:

答案 0 :(得分:2)

观察到代码并行运行比串行运行慢,这告诉我罪魁祸首很可能是false sharing

SCATT数组为shared,每个线程都会访问其中的一部分,以便读取和写入。您的代码中没有竞争条件,但是写入相同数组的线程(尽管是不同的切片)会使速度变慢。

原因是每个线程在缓存中加载数组SCATT的一部分,并且每当另一个线程写入SCATT的那部分时,这使先前存储在缓存中的数据无效。虽然输入数据没有改变,因为没有竞争条件(另一个线程更新了SCATT的不同切片),处理器得到一个缓存无效的信号,从而重新加载数据(详见上面的链接) )。这会导致高数据传输开销。

解决方案是使每个切片对给定线程都是私有的。在您的情况下,它甚至更简单,因为您根本不需要读取SCATT。只需替换

SCATT(IN1,IN2) = DCOMPLX(SCATREL, SCATIMG)
UADI(IN1,IN2) = SCATT(IN1,IN2)+1.0

SCATT0 = DCOMPLX(SCATREL, SCATIMG)
UADI(IN1,IN2) = SCATT0+1.0
SCATT(IN1,IN2) = SCATT0

其中SCATT0private变量。

为什么在第一个片段中不会发生这种情况?当然,我怀疑编译器可能已经优化了问题。在计算DATA((IN2-1)*NN(1)+IN1)/(NN(1)*NN(2))时,它很可能将其存储在寄存器中,并在SCATT(IN1,IN2)中使用此值而不是UADI(IN1,IN2) = SCATT(IN1,IN2)+1.0

此外,如果你想加快代码速度,你应该让循环更有效率。并行化的第一个规则是不要这样做!首先优化串行代码。因此,将片段1替换为(您甚至可以在最后一行的workshare内)

DATA/(NN(1)*NN(2))
!$OMP PARALLEL DO private(temp)
DO IN2=1,NN(2)
    temp = (IN2-1)*NN(1)
    SCATT(:,IN2) = DATA(temp+1:temp+NN(1))
ENDDO
!$OMP END PARALLEL DO
UADI = SCATT+1.0

您也可以使用代码段2执行类似操作。