这两者有什么区别?
[一种]
#pragma omp parallel
{
#pragma omp for
for(int i = 1; i < 100; ++i)
{
...
}
}
[乙]
#pragma omp parallel for
for(int i = 1; i < 100; ++i)
{
...
}
这两者有什么区别?
[一种]
#pragma omp parallel
{
#pragma omp for
for(int i = 1; i < 100; ++i)
{
...
}
}
[乙]
#pragma omp parallel for
for(int i = 1; i < 100; ++i)
{
...
}
这些是等价的。
#pragma omp parallel
产生一组线程,同时#pragma omp for
在产生的线程之间划分循环迭代。#pragma omp parallel for
你可以使用 fused指令同时做这两件事。
我认为没有任何区别,一个是另一个的捷径。尽管您的确切实现可能会以不同的方式处理它们。
组合并行工作共享构造是指定包含一个工作共享构造但不包含其他语句的并行构造的快捷方式。允许的子句是允许并行和工作共享结构的子句的联合。
取自http://www.openmp.org/mp-documents/OpenMP3.0-SummarySpec.pdf
OpenMP 的规格在这里:
这是使用分离parallel
和for
here的示例。简而言之,它可用于for
在多个线程中执行循环之前动态分配 OpenMP 线程私有数组。以防万一,不可能进行相同的初始化parallel for
。
UPD:在问题示例中,单个 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
}
尽管具体示例的两个版本是等效的,但正如其他答案中已经提到的那样,它们之间仍然存在一个小的差异。第一个版本包括一个不必要的隐式障碍,在“omp for”的末尾遇到。另一个隐式障碍可以在平行区域的末端找到。将“nowait”添加到“omp for”将使这两个代码等效,至少从 OpenMP 的角度来看是这样。我提到这一点是因为 OpenMP 编译器可能会为这两种情况生成稍微不同的代码。
显然有很多答案,但是这个答案很好(附来源)
#pragma omp for
仅将循环的一部分委托给当前团队中的不同线程。团队是执行程序的一组线程。在程序开始时,团队仅由一个 成员组成:运行程序的主线程。要创建一个新的线程组,您需要指定 parallel 关键字。它可以在周围的上下文中指定:
#pragma omp parallel { #pragma omp for for(int n = 0; n < 10; ++n) printf(" %d", n); }
和:
什么是:并行,为和一个团队
parallel、parallel for和for的区别如下:
团队是当前执行的线程组。在程序开始时,团队由一个线程组成。在下一个块/语句的持续时间内,并行构造将当前线程拆分为一个新的线程组,之后该组重新合并为一个。for 将 for 循环的工作分配给当前团队的线程。
它不创建线程,它只在当前执行团队的线程之间分配工作。parallel for 是同时表示两个命令的简写:parallel 和 for。Parallel 创建了一个新团队,并拆分该团队以处理循环的不同部分。如果您的程序从不包含并行结构,则线程不会超过一个;启动程序并运行它的主线程,就像在非线程程序中一样。
当我在 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
and use #pragma omp parallel
,运行时间会达到 179 毫秒,这比串行代码慢。(机器硬件并发为8)
代码链接到libgomp
TL;DR:唯一的区别是第一个代码调用2 个隐式障碍,而第二个代码只有1 个。
使用现代官方 OpenMP 5.1 标准作为参考的更详细的答案。
OpenMP 子句:
#pragma omp parallel
创建一个parallel region
包含 的团队threads
,其中每个线程将执行并行区域所包含的整个代码块。
从OpenMP 5.1可以阅读更正式的描述:
当线程遇到并行构造时,会创建一组线程来执行并行区域 (..)。遇到并行构造的线程成为新团队的主线程,在新并行区域的持续时间内线程数为零。新团队中的所有线程,包括主线程,都执行该区域。创建团队后,团队中的线程数在该并行区域的持续时间内保持不变。
这:
#pragma omp parallel for
创建一个并行区域(如前所述),并使用默认值和(通常为threads
)为该区域分配它所包含的循环的迭代。但是请记住,这些默认值可能因标准的不同具体实施而异。chunk size
schedule
static
OpenMP
从OpenMP 5.1您可以阅读更正式的描述:
工作共享循环结构指定一个或多个相关循环的迭代将由团队中的线程在其隐式任务的上下文中并行执行。迭代分布在执行工作共享循环区域绑定到的并行区域的团队中已经存在的线程中。
此外,
并行循环构造是指定并行构造的快捷方式,该并行构造包含具有一个或多个关联循环且没有其他语句的循环构造。
或非正式地,#pragma omp parallel for
是构造函数#pragma omp parallel
与#pragma omp for
.
对于您展示的两个版本,如果一个使用chunk_size=1
和 静态 schedule
,则执行流程将导致如下内容:
在代码方面,循环将被转换为逻辑上类似于:
for(int i=omp_get_thread_num(); i < n; i+=omp_get_num_threads())
{
//...
}
omp_get_thread_num 例程返回当前团队中调用线程的线程号。
返回当前团队中的线程数。在程序的连续部分中,omp_get_num_threads 返回 1。
或者换句话说,for(int i = THREAD_ID; i < n; i += TOTAL_THREADS)
。THREAD_ID
范围从0
到TOTAL_THREADS - 1
,表示在TOTAL_THREADS
并行区域上创建的团队的线程总数。