omp 并行与 omp 并行

Posted

技术标签:

【中文标题】omp 并行与 omp 并行【英文标题】:omp parallel vs. omp parallel for 【发布时间】:2010-11-29 17:13:50 【问题描述】:

这两者有什么区别?

[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)

   ...

【问题讨论】:

【参考方案1】:

这些是等价的。

#pragma omp parallel 产生一组线程,而#pragma omp for 在产生的线程之间划分循环迭代。你可以使用 fused #pragma omp parallel for 指令同时做这两件事。

【讨论】:

在我的代码中,我正在使用这种结构。但是,当我在 for 指令中使用 schedule(static, chunk) 子句时,我遇到了问题。代码运行良好,但是当我从 MPI 程序调用此代码时,它会进入无限循环。在此循环的所有迭代中,循环计数器都为零。我在#pragma omp parallel 指令中将循环计数器定义为私有。不知道为什么它只在 MPI 调用代码时失败。如果这很重要,我有点确定每个 MPI 进程都在集群的不同处理器上运行。不知道是不是日程安排导致了问题。 当我使用#pragma omp parallel for 指令时,同样的事情工作正常。应该有一些区别。 更新:事实证明,我只在使用 schedule 子句时才观察到这个问题,所以我猜这不取决于我是使用组合并行 for 还是两个不同的指令。【参考方案2】:

我认为没有什么区别,一个是另一个的捷径。尽管您的确切实现可能会以不同的方式处理它们。

组合的并行工作共享结构是 指定包含一个工作共享构造的并行构造 并且没有其他声明。允许的子句是子句的并集 允许并行和工作共享结构。

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

OpenMP 的规格在这里:

https://openmp.org/specifications/

【讨论】:

【参考方案3】:

这里是使用分隔的parallelfor here 的示例。简而言之,它可用于在多个线程中执行for 循环之前动态分配OpenMP 线程私有数组。 在parallel for 的情况下不可能做同样的初始化。

更新: 在问题示例中,单个 pragma 和两个 pragma 之间没有区别。但在实践中,您可以使用分离的并行和 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

【讨论】:

【参考方案4】:

尽管具体示例的两个版本是等效的,但正如其他答案中已经提到的那样,它们之间仍然存在一个小的差异。第一个版本包括一个不必要的隐式障碍,在“omp for”的末尾遇到。另一个隐式障碍可以在平行区域的末端找到。将“nowait”添加到“omp for”将使这两个代码等效,至少从 OpenMP 的角度来看是这样。我提到这一点是因为 OpenMP 编译器可能会为这两种情况生成稍微不同的代码。

【讨论】:

【参考方案5】:

显然有很多答案,但是这个答案很好(附来源)

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

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

#pragma omp parallel

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

和:

什么是:并行、为和团队

并行的区别, 并行for和for如下:

团队是线程的组 当前执行的。在计划开始时,团队由 一个线程。并行构造将当前线程拆分为 在下一个块/语句期间的新线程组, 之后,团队重新合二为一。对于划分的工作 在当前团队的线程中循环。

它不会创建 线程,它只在当前线程之间分配工作 执行团队。 parallel for 是一次两个命令的简写: 平行和为。 Parallel 创建了一个新团队,对于拆分 团队来处理循环的不同部分。如果你的程序从来没有 包含一个并行结构,线程永远不会超过一个; 启动程序并运行它的主线程,如 非线程程序。

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

【讨论】:

【参考方案6】:

当我在 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

【讨论】:

我认为这是因为 omp parallel 在单独的线程中执行循环而不将其划分为线程,所以主线程正在等待第二个线程完成。以及花在同步上的时间。 那是因为没有#pragma omp for 根本就没有多线程共享循环。但无论如何,这不是 OPs 的情况,请在 #pragm omp parallel 内再试一次额外的 #pragma omp for,它应该像 #pragma omp parallel for 版本一样运行类似(如果不相同)。 我认为这个答案是最好的,因为它表明它们不是“等价的”【参考方案7】:

TL;DR:唯一的区别在于第一个代码调用2隐式屏障,而第二只有1


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

OpenMP 子句:

#pragma omp parallel

创建一个包含threads 团队的parallel region,其中每个线程将执行并行区域 所包含的整个代码块。

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

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

#pragma omp parallel for 

创建一个平行区域(如前所述),并使用默认的chunk size 和@987654334 为该区域的threads 分配它所包含的循环的迭代@(通常static)。但是请记住,这些默认值可能因OpenMP 标准的不同具体实现而异。

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

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

Moreover,

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

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

对于您展示的两个版本,如果一个使用 chunk_size=1static schedule,执行流程将导致如下结果:

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

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 &lt; n; i += TOTAL_THREADS)。其中THREAD_ID 范围从0TOTAL_THREADS - 1TOTAL_THREADS 表示在并行区域上创建的团队的线程总数。

【讨论】:

以上是关于omp 并行与 omp 并行的主要内容,如果未能解决你的问题,请参考以下文章

嵌套并行 omp v. 2.0 性能

在 OpenMP 中,我们如何并行运行多个代码块,其中每个代码块包含 omp single 和 omp for 循环?

在另一个并行循环中调用函数时,函数中的“pragma omp parallel for”无效

了解#pragma omp 并行

带有 pragma omp 并行的嵌套循环,混乱起来

omp_get_max_threads() 在并行区域返回 1,但应该是 8