我正在尝试创建一个创建数组的程序,并使用OpenMP为该数组中的每个位置分配值。这将是微不足道的,除了我想指定一个数组负责的位置。
例如,如果我有一个长度为80和8个线程的数组,我想确保线程0只写入位置0-9,线程1到10-19,依此类推。
我是OpenMP的新手,所以我尝试了以下内容:
#include <omp.h>
#include <stdio.h>
#define N 80
int main (int argc, char *argv[])
{
int nthreads = 8, tid, i, base, a[N];
#pragma omp parallel
{
tid = omp_get_thread_num();
base = ((float)tid/(float)nthreads) * N;
for (i = 0; i < N/nthreads; i++) {
a[base + i] = 0;
printf("%d %d\n", tid, base+i);
}
}
return 0;
}
然而,正如我所料,该计划并未访问所有职位。每次运行时输出都不同,例如:
4 40
5 51
5 52
5 53
5 54
5 55
5 56
5 57
5 58
5 59
5 50
4 40
6 60
6 60
3 30
0 0
1 10
我想我错过了一个指令,但我不知道它是哪一个。
答案 0 :(得分:1)
你错过了一点点。指令
#pragma omp parallel
仅告知运行时以下代码块将并行执行,主要由所有线程执行。但它没有指定跨线程共享工作,只是所有线程都要执行块。要分享工作,您的代码将需要另一个指令,如此
#pragma omp parallel
{
#pragma omp for
...
这是for
指令,它跨线程分配工作。
但是,您在程序设计中犯了一个错误,这比您对OpenMP语法的不熟悉更严重。正如您所建议的那样,跨线程手动分解工作正是OpenMP旨在帮助程序员避免的。通过尝试自己进行分解,您正在编写OpenMP的编程并运行两个风险:
如果您想对线程的工作分配进行一些控制,请调查schedule
子句。我建议你开始你的并行区域(注意我将这两个指令融合到一个语句中):
#pragma omp parallel for default(none) shared(a,base,N)
{
for (i = 0; i < N; i++) {
a[base + i] = 0;
}
另请注意,我已指定变量的可访问性。这是一个很好的做法,特别是在学习OpenMP时。编译器会自动使i
私有。
正如我所写的那样,运行时将把i
上的迭代分成块,每个线程一个。第一个帖子将获得i = 0..N/num_threads
,第二个帖子将获得i = (N/num_threads)+1..2N/num_threads
,依此类推。
稍后您可以明确地向该指令添加schedule
子句。我上面写的相当于
#pragma omp parallel for default(none) shared(a,N) schedule(static)
但您也可以尝试
#pragma omp parallel for default(none) shared(a,N) schedule(dynamic,chunk_size)
以及许多其他选项,这些选项在通常的地方都有很好的记录。
答案 1 :(得分:1)
确保事物以你想要的方式工作的方法是使用一个只有8次迭代的循环作为外部(并行)循环,并让每个线程执行一个内部循环来访问正确的元素:
#pragma omp parallel for private(j)
for(i = 0; i < 8; i++) {
for(j = 0; j < 10; j++) {
a[10*i+j] = 0;
printf("thread %d updated element %d\n", omp_get_thread_num(), 8*i+j);
}
}
我现在无法对此进行测试,但我90%确定这完全符合您的要求(当您这样做时,您可以“完全控制”工作原理)。然而,它可能不是最有效的事情。首先 - 当你只想将一堆元素设置为零时,你想要使用像memset
这样的内置函数,而不是循环......
答案 2 :(得分:0)
#pragma omp parallel
不足以使for
循环并行化。
for
循环参数的竞争条件。
如果我没记错的话,任何在并行区域之外声明的变量都会在线程之间共享。所以所有线程一次写入i
,tid
和base
。您可以使用适当的private
/ shared
条款。
然而,更好的方法是让OpenMP分配工作。 这就足够了:
#pragma omp parallel private(tid)
{
tid = omp_get_thread_num();
#pramga omp for
for (i = 0; i < N; i++) {
a[i] = 0;
printf("%d %d\n", tid, i);
}
}
请注意private(tid)
它为每个线程制作tid
的本地副本,因此它们不会在omp_get_thread_num()
上相互覆盖。此外,可以声明shared(a)
,因为我们希望每个线程都在同一个表副本上工作。这是隐含的。我认为迭代器应该被声明为私有,但我认为pragma
负责处理它,而不是100%它是如何在这个特定情况下,当它在并行区域之外声明时。但我确信你可以手动将它设置为shared
然后搞砸了。