OpenMP 嵌套 for 循环给出意外结果
OpenMP nested for loop gives unexpected result
我正在学习 OpenMP,下面带有嵌套 for 循环的代码在线程数 > 1 的情况下给出了意外结果。我希望这里只有外部循环会被并行化,我希望有 4 行输出。我正在使用 gcc 4.8.4.
#pragma omp parallel
{
const int nthreads = omp_get_num_threads();
const int ithread = omp_get_thread_num();
#pragma omp for
for (out = 0; out < 2; out++) {
for (in = 0; in < 2; in++) {
printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in);
}
}
}
如果我设置 OMP_NUM_THREADS=1
,我会得到 4 行的预期输出:
id= 0 of 1, out= 0, in= 0
id= 0 of 1, out= 0, in= 1
id= 0 of 1, out= 1, in= 0
id= 0 of 1, out= 1, in= 1
但是,OMP_NUM_THREADS=2
漏掉了一行输出!
id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1
将内部循环设置为 for (in = 0; in < 3; in++)
只给出 4 行输出而不是预期的 6 行!
id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1
id= 1 of 2, out= 1, in= 2
我是不是做错了什么?请帮我解决这个问题。谢谢。
看起来您的内部循环变量似乎是在并行块开始之前声明的。这意味着它在所有线程之间隐式共享。也许您正在尝试并行化 2D 循环,但您尝试的方法无法实现;相反,每个内部循环将以不确定的方式与其他 运行 内部循环冲突。
如果您想并行化一个 2D 循环,您必须对所有元素使用一个循环,然后手动计算索引,例如for(int i=0;i<2*2;i++){ int out=i/2; int in=i%2; ... }
。现在您可以毫无问题地使 i
并行循环。正如 High Performance Mark 指出的那样, 有一个专门的构造:OpenMP and nested loops?
如果您可以只并行化一个维度(这通常就足够了),您要么需要在并行循环中声明内部变量:
int out;
#pragma omp parallel for
for(out=0;out<2;out++){
int in;
for(in=0;in<2;in++){
// action
}
}
或者显式地将内部变量标记为私有:
int in, out;
#pragma omp parallel for private(in)
for(out=0;out<2;out++){
for(in=0;in<2;in++){
// action
}
}
这些都使外循环并行,内循环对每个线程都是私有的,防止一个线程的当前状态改变另一个线程的状态。如果您使用的是 c99 之前的 c 语言,则第二种风格很有用,但除此之外,我建议只将声明移动到循环内更利于显示意图。
这个问题包含一些关于设置私有变量的有用注释:OpenMP: are local variables automatically private?
最后,你应该避免直接从多个线程打印到同一个输出;这些函数的线程安全是模棱两可的(尽管特别是在 Linux 上似乎定义得更好)。最好把打印抽象成一个线程安全的缓冲区,用单线程写缓冲区。
我看到两个问题:
- 我看不到第一个 omp parallel 语句的用途,应该将其删除
in 应该声明为私有的(这样每个线程都有自己的副本)
#pragma omp parallel for private(in) /* each thread needs its own copy of in */
for (out = 0; out < 2; out++) {
for (in = 0; in < 2; in++) {
printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in);
}
}
}
我正在学习 OpenMP,下面带有嵌套 for 循环的代码在线程数 > 1 的情况下给出了意外结果。我希望这里只有外部循环会被并行化,我希望有 4 行输出。我正在使用 gcc 4.8.4.
#pragma omp parallel
{
const int nthreads = omp_get_num_threads();
const int ithread = omp_get_thread_num();
#pragma omp for
for (out = 0; out < 2; out++) {
for (in = 0; in < 2; in++) {
printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in);
}
}
}
如果我设置 OMP_NUM_THREADS=1
,我会得到 4 行的预期输出:
id= 0 of 1, out= 0, in= 0
id= 0 of 1, out= 0, in= 1
id= 0 of 1, out= 1, in= 0
id= 0 of 1, out= 1, in= 1
但是,OMP_NUM_THREADS=2
漏掉了一行输出!
id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1
将内部循环设置为 for (in = 0; in < 3; in++)
只给出 4 行输出而不是预期的 6 行!
id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1
id= 1 of 2, out= 1, in= 2
我是不是做错了什么?请帮我解决这个问题。谢谢。
看起来您的内部循环变量似乎是在并行块开始之前声明的。这意味着它在所有线程之间隐式共享。也许您正在尝试并行化 2D 循环,但您尝试的方法无法实现;相反,每个内部循环将以不确定的方式与其他 运行 内部循环冲突。
如果您想并行化一个 2D 循环,您必须对所有元素使用一个循环,然后手动计算索引,例如 有一个专门的构造:OpenMP and nested loops?for(int i=0;i<2*2;i++){ int out=i/2; int in=i%2; ... }
。现在您可以毫无问题地使 i
并行循环。正如 High Performance Mark 指出的那样,
如果您可以只并行化一个维度(这通常就足够了),您要么需要在并行循环中声明内部变量:
int out;
#pragma omp parallel for
for(out=0;out<2;out++){
int in;
for(in=0;in<2;in++){
// action
}
}
或者显式地将内部变量标记为私有:
int in, out;
#pragma omp parallel for private(in)
for(out=0;out<2;out++){
for(in=0;in<2;in++){
// action
}
}
这些都使外循环并行,内循环对每个线程都是私有的,防止一个线程的当前状态改变另一个线程的状态。如果您使用的是 c99 之前的 c 语言,则第二种风格很有用,但除此之外,我建议只将声明移动到循环内更利于显示意图。
这个问题包含一些关于设置私有变量的有用注释:OpenMP: are local variables automatically private?
最后,你应该避免直接从多个线程打印到同一个输出;这些函数的线程安全是模棱两可的(尽管特别是在 Linux 上似乎定义得更好)。最好把打印抽象成一个线程安全的缓冲区,用单线程写缓冲区。
我看到两个问题:
- 我看不到第一个 omp parallel 语句的用途,应该将其删除
in 应该声明为私有的(这样每个线程都有自己的副本)
#pragma omp parallel for private(in) /* each thread needs its own copy of in */ for (out = 0; out < 2; out++) { for (in = 0; in < 2; in++) { printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in); } }
}