使用OpenMP的显式刷新指令:何时需要以及何时有用

时间:2013-10-30 15:36:20

标签: openmp

我从未使用过的一个OpenMP指令,不知道何时使用flush(有和没有列表)。

我有两个问题:

1.) When is an explicit `omp flush` or `omp flush(var1, ...) necessary?  
2.) Is it sometimes not necessary but helpful (i.e. can it make the code fast)?

我无法理解何时使用显式刷新的主要原因是flushes are done implicitly after many directives (e.g. as barrier, single, ...) 同步线程。例如,我不能看到使用flush的方式而不是同步(例如使用nowait)会有所帮助。

我知道不同的编译器可能以不同的方式实现omp flush。有些人可能会将列表中的刷新解释为没有列表(即刷新所有共享对象)OpenMP flush vs flush(list)。但我只关心规范的要求。换句话说,我想知道原则上明确的flush可能是必要的还是有用的。

编辑:我想我需要澄清我的第二个问题。让我举个例子。我想知道是否存在删除隐式刷新的情况(例如使用nowait)而是使用显式刷新而不是仅对某些共享变量更快(并且仍然给出正确的结果)。如下所示:

float a,b;
#pragma omp parallel
{
    #pragma omp for nowait // No barrier.  Do not flush on exit.
        //code which uses only shared variable a        
    #pragma  omp flush(a) // Flush only variable a rather than all shared variables.       
    #pragma omp for
       //Code which uses both shared variables a and b.
}

我认为代码在第一个for循环之后仍然需要一个障碍,但是所有障碍都有一个隐式刷新,因此无法实现目的。是否有可能没有冲洗的屏障?

1 个答案:

答案 0 :(得分:15)

flush指令告诉OpenMP编译器生成代码以使共享内存上的线程私有视图再次一致。 OpenMP通常可以很好地处理这个问题,并为典型程序做正确的事情。因此,不需要flush

但是,有些情况下OpenMP编译器需要一些帮助。其中一种情况是当您尝试实现自己的旋转锁定时。在这些情况下,您需要组合使用刷新才能使工作正常,否则旋转变量将不会更新。让冲洗顺序正确是非常困难的,并且非常非常容易出错。

一般建议不应使用刷新。如果有的话,程序员应该尽量避免使用列表(flush(var,...))。有些人实际上是在谈论在将来的OpenMP中弃用它。

性能方面,同花顺的影响应该是积极的,而不是积极的。由于它会导致编译器生成内存栅栏和额外的加载/存储操作,我希望它会减慢速度。

编辑:对于你的第二个问题,答案是否定的。 OpenMP确保每个线程在需要时在共享内存上具有一致的视图。如果线程不同步,则不需要更新共享内存上的视图,因为它们看不到任何“有趣”的更改。这意味着线程的任何读取都不会读取任何其他线程已更改的数据。如果是这种情况,那么你的程序中就会出现竞争条件和潜在的错误。为避免竞争,您需要进行同步(这意味着刷新以使每个参与线程的视图再次保持一致)。类似的论点也适用于障碍。您可以使用障碍在计算并行区域时启动新纪元。由于您将线程保持在锁定步骤中,因此您很可能在上一个时期计算的线程之间也存在一些共享状态。

BTW,OpenMP 可以保留一个线程的私有数据,但它不拥有。因此,OpenMP编译器可能会将变量保留在寄存器中一段时间​​,这会导致它们与共享内存不同步。但是,对数组元素的更新通常很快会在共享内存中反映出来,因为线程的私有存储量通常很小(寄存器集,高速缓存,临时存储器等)。 OpenMP只能为您提供一些弱限制。实际的OpenMP实现(或硬件)可能与它希望的一样严格(例如,立即写回任何更改并一直刷新)。