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】:这里是使用分隔的parallel
和for
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=1
和 static 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 < n; i += TOTAL_THREADS)
。其中THREAD_ID
范围从0
到TOTAL_THREADS - 1
,TOTAL_THREADS
表示在并行区域上创建的团队的线程总数。
【讨论】:
以上是关于omp 并行与 omp 并行的主要内容,如果未能解决你的问题,请参考以下文章
在 OpenMP 中,我们如何并行运行多个代码块,其中每个代码块包含 omp single 和 omp for 循环?