在并行区域中使用OpenMP迭代for循环

6

抱歉,如果标题有点不清楚。我不太知道该怎么表达。

我想知道是否有任何方法可以做到以下几点:

#pragma omp parallel
{
    for (int i = 0; i < iterations; i++) {
        #pragma omp for
        for (int j = 0; j < N; j++)
            // Do something
    }
}

忽略在for循环中省略私有限定符等内容,是否有办法可以在外部循环之外分叉线程,以便只并行化内部循环?据我了解(如有错误请纠正),所有线程都将执行外部循环。我对内部循环的行为不确定,但我认为 for 会将块分配给遇到它的每个线程。

我想做的是不必对迭代进行fork/join操作,而只需在外部循环中执行一次即可。这是正确的策略吗?

如果还有另一个不应并行化的外部循环呢?也就是说...

#pragma omp parallel
{

    for (int i = 0; i < iterations; i++) {
        for(int k = 0; k < innerIterations; k++) {
            #pragma omp for
            for (int j = 0; j < N; j++)
                // Do something

            // Do something else
        }
    }
}

希望有人能够给我指出使用OpenMP并行化大型应用程序的示例,这样我就可以更好地理解在使用OpenMP时要采用的策略。但我似乎找不到任何相关示例。 澄清一下:我正在寻找不改变循环顺序或涉及阻塞、缓存和一般性能考虑因素的解决方案。我想了解如何在OpenMP中按照指定的循环结构完成此操作。// Do something 可能有依赖关系,假设它们存在并且您不能移动它们。

也许你可以举个例子说明你想做什么。我的意思是在代码中填写//做某事。 - user2088790
@raxman,那样做没有帮助。这是一个针对此类问题的通用解决方案请求,而不是特定应用程序的解决方案。 - Pochi
你可以继续点赞/接受某些答案。看起来人们付出了一些努力,但得到的点赞很少。 - the swine
3个回答

4

我认为你处理两个for循环的方式是正确的,因为它实现了你想要的行为:外部循环没有并行化,而内部循环是。

为了更好地说明发生了什么,我将尝试向你的代码添加一些注释:

#pragma omp parallel
{
  // Here you have a certain number of threads, let's say M
  for (int i = 0; i < iterations; i++) {
        // Each thread enters this region and executes all the iterations 
        // from i = 0 to i < iterations. Note that i is a private variable.
        #pragma omp for
        for (int j = 0; j < N; j++) {
            // What happens here is shared among threads so,
            // according to the scheduling you choose, each thread
            // will execute a particular portion of your N iterations
        } // IMPLICIT BARRIER             
  }
}

隐式屏障是线程等待彼此的同步点。一般来说,更好的做法是并行化外部循环而不是内部循环,因为这将为iterations*N个迭代创建一个单一的同步点(而不是上面所创建的iterations个点)。


外层循环应该指定某个算法的多次迭代,因此无法并行化。如果我没有表达清楚,对此感到抱歉。 - Pochi
1
外层循环没有并行化,因为它上面没有工作共享指令。 - Massimiliano
如果您使用我建议的代码运行“hello world printf”测试,它会显示所有这些内容。您可以看到,如果添加nowait标签,则屏障将被移除。换句话说,没有nowait时,外部循环不会并行化,而有了它就会并行化。 - user2088790
@raxman 外层循环__永远不会__并行化。使用nowait子句可以消除同步点,仅此而已。 - Massimiliano
好的,但也许这是术语问题,因为无论哪种情况下外层循环都在不同的线程上运行。它以单线程方式(未并行化)运行的唯一方法是将OMP编译指示放在外层循环内部。 - user2088790
我想我明白你的意思。你的意思是迭代永远不会在外部循环的线程之间分配。所以我写的是错误的。尽管如此,外部循环仍然在不同的线程中运行。 - user2088790

1
我不确定我能回答你的问题。我只使用OpenMP几个月,但当我尝试回答这样的问题时,我会做一些hello world printf测试,如下所示。我认为这可能有助于回答你的问题。还可以尝试#pragma omp for nowait,看看会发生什么。
只需确保在 "// Do something and // Do something else" 时,您不要写入相同的内存地址并创建竞争条件。此外,如果您正在进行大量读写操作,则需要考虑如何有效地使用缓存。
#include "stdio.h"
#include <omp.h>
void loop(const int iterations, const int N) {
    #pragma omp parallel
    {
        int start_thread = omp_get_thread_num();
        printf("start thread %d\n", start_thread);
        for (int i = 0; i < iterations; i++) {
            printf("\titeration %d, thread num %d\n", i, omp_get_thread_num());
            #pragma omp for
            for (int j = 0; j < N; j++) {
                printf("\t\t inner loop %d, thread num %d\n", j, omp_get_thread_num());
            }
        }
    }
}

int main() {
    loop(2,30);
}

在性能方面,您可能希望考虑像这样融合您的循环。
#pragma omp for
for(int n=0; n<iterations*N; n++) {
    int i = n/N;
    int j = n%N;    
    //do something as function of index i and j
}

1

由于代码中的依赖关系不同,因此很难回答。但是一般解决方法是倒置循环的嵌套方式,如下所示:

#pragma omp parallel
{
    #pragma omp for
    for (int j = 0; j < N; j++) {
        for (int i = 0; i < iterations; i++) {
            // Do something
        }
    }
}

当然,这取决于循环内的代码,可能有可能不可能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接