omp parallel与omp parallel for

时间:2009-09-19 10:52:19

标签: openmp

这两者有什么区别?

[A]

#pragma omp parallel
{ 
    #pragma omp for
    for(int i = 1; i < 100; ++i)
    {
        ...
    }
}

[B]

#pragma omp parallel for
for(int i = 1; i < 100; ++i)
{
   ...
}

7 个答案:

答案 0 :(得分:59)

我认为没有任何区别,一个是另一个的捷径。虽然您的确切实施可能会以不同方式处理它们

  

组合的并行工作共享结构是一种快捷方式   指定包含一个工作共享构造的并行构造   而且没有其他声明。允许的条款是条款的结合   允许并行和工作共享结构。

取自http://www.openmp.org/mp-documents/OpenMP3.0-SummarySpec.pdf

OpenMP的规范如下:

https://openmp.org/specifications/

答案 1 :(得分:56)

这些是等效的。

#pragma omp parallel产生一组线程,而#pragma omp for在产生的线程之间划分循环迭代。您可以使用融合的#pragma omp parallel for指令同时执行这两项操作。

答案 2 :(得分:24)

以下是使用分隔parallelfor here的示例。简而言之,它可以在多个线程中执行for循环之前用于动态分配OpenMP线程专用数组。 在parallel for案例中进行相同的初始化是不可能的。

UPD: 在问题示例中,单个pragma和两个pragma之间没有区别。但实际上,您可以使用分离的parallel和for指令来创建更多线程感知行为。 一些代码例如:

#pragma omp parallel
{ 
    double *data = (double*)malloc(...); // this data is thread private

    #pragma omp for
    for(1...100) // first parallelized cycle
    {
    }

    #pragma omp single 
    {} // make some single thread processing

    #pragma omp for // second parallelized cycle
    for(1...100)
    {
    }

    #pragma omp single 
    {} // make some single thread processing again

    free(data); // free thread private data
}

答案 3 :(得分:8)

虽然具体示例的两个版本都是等效的,但正如其他答案中已经提到的那样,它们之间仍然存在一个小的差异。第一个版本包含一个不必要的隐含障碍,在&#34; omp结束时遇到&#34;。可以在并行区域的末尾找到另一个隐式屏障。添加&#34; nowait&#34; &#34; omp for&#34;会使两个代码等效,至少从OpenMP的角度来看。我之所以提到这一点,是因为OpenMP编译器可能会为这两种情况生成略有不同的代码。

答案 4 :(得分:5)

当我在g ++ 4.7.0中进行for循环时,我看到了截然不同的运行时 并使用

std::vector<double> x;
std::vector<double> y;
std::vector<double> prod;

for (int i = 0; i < 5000000; i++)
{
   double r1 = ((double)rand() / double(RAND_MAX)) * 5;
   double r2 = ((double)rand() / double(RAND_MAX)) * 5;
   x.push_back(r1);
   y.push_back(r2);
}

int sz = x.size();

#pragma omp parallel for

for (int i = 0; i< sz; i++)
   prod[i] = x[i] * y[i];

串行代码(无openmp)在79毫秒内运行。 “并行”代码在29毫秒内运行。 如果我省略for并使用#pragma omp parallel,则运行时最长可达179毫秒, 这比串行代码慢。 (该机器的并发性为8)

代码链接到libgomp

答案 5 :(得分:4)

显然有很多答案,但是这个答案很好(带有源代码)

  

#pragma omp for仅将循环的一部分委派给   当前团队中的不同线程。团队是线程组   执行程序。在计划开始时,团队仅包含一个   单个成员:运行程序的主线程

     

要创建新的线程组,您需要指定并行   关键词。可以在周围的上下文中指定它:

#pragma omp parallel
{
   #pragma omp for
   for(int n = 0; n < 10; ++n)
   printf(" %d", n);
}

和:

  

什么是:平行的,为了和一个团队

     

平行,   和for并行,如下所示:

     

团队是线程组   当前执行的。在计划开始时,团队包括   一个线程。并行构造将当前线程拆分为一个   在下一个阻止/声明的持续时间内有新的线程组,   之后,团队合并为一体。为划分工作   在当前团队的线程中进行循环。

     

它不会创建   线程,它仅将工作分配给当前线程   执行团队。 parallel for是一次两个命令的简写:   并行和。 Parallel创建了一个新团队,并进行了分组   团队来处理循环的不同部分。如果您的程序从未   包含一个并行构造,线程永远不止一个;   启动程序并运行它的主线程,如   非线程程序。

https://bisqwit.iki.fi/story/howto/openmp/

答案 6 :(得分:0)

TL;DR:唯一的区别在于第一个代码调用了两个隐式障碍,而第二个代码只调用了一个。


使用官方 OpenMP 5.1 标准作为参考的更详细答案。

#pragma omp parallel

将创建一个由 parallel region 组成的团队的 threads,其中每个线程将执行 parallel region 包含的整个代码块。

OpenMP 5.1 可以阅读更正式的描述:

<块引用>

当一个线程遇到并行结构时,一组线程 创建以执行并行区域 (..)。这 遇到并行构造的线程成为主线程 新团队的线程,持续时间内线程数为零 新的平行区域。 新团队中的所有线程,包括 主线程,执行区域。 创建团队后, 团队中的线程数在持续时间内保持不变 那个平行区域。

#pragma omp parallel for

将创建一个 parallel region(如前所述),并且将使用 threads 将其包含的循环的迭代分配给该区域的 default chunk size,并且default schedule 这是通常 static。但是请记住,default schedule 可能因 OpenMP 标准的不同具体实现而异。

您可以从 OpenMP 5.1 中阅读更正式的描述:

<块引用>

worksharing-loop 结构指定一个或 更多关联的循环将由线程并行执行 团队在他们隐性任务的背景下。 迭代是 分布在团队中已经存在的线程中 执行工作共享循环区域所在的并行区域 绑定

Moreover,

<块引用>

并行循环结构是指定并行循环的快捷方式 包含具有一个或多个关联的循环结构的结构 循环,没有其他语句。

或者非正式地说,#pragma omp parallel for 是构造函数 #pragma omp parallel#pragma omp for 的组合。

您拥有的带有 chunk_size=1static schedule 的两个版本都会导致类似:

enter image description here

在代码方面,循环将被转换为逻辑上类似于:

for(int i=omp_get_thread_num(); i < n; i+=omp_get_num_threads())
{  
    //...
}

哪里omp_get_thread_num()

<块引用>

omp_get_thread_num 例程返回线程号,在 调用线程的当前团队。

omp_get_num_threads()

<块引用>

返回当前团队中的线程数。在一个连续的 omp_get_num_threads 程序部分返回 1。

或者换句话说,for(int i = THREAD_ID; i < n; i += TOTAL_THREADS)。其中 THREAD_ID 的范围从 0TOTAL_THREADS - 1TOTAL_THREADS 表示在并行区域上创建的团队线程总数。