OpenMP 嵌套循环任务并行性,计数器未给出正确结果
OpenMP nested loop task parallelism, counter not giving correct result
我是 openMP 的新手。我正在尝试使用任务处理并行化嵌套循环,但它没有给我正确的计数器输出。顺序输出为“总像素 = 100000000”。有人可以帮我吗?
注意:我使用 #pragma omp parallel for reduction (+:pixels_inside) private(i,j) 完成了此操作。这很好用,现在我想使用任务。
到目前为止我尝试了什么:
#include<iostream>
#include<omp.h>
using namespace std;
int main(){
int total_steps = 10000;
int i,j;
int pixels_inside=0;
omp_set_num_threads(4);
//#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
#pragma omp parallel
#pragma omp single private(i)
for(i = 0; i < total_steps; i++){
#pragma omp task private(j)
for(j = 0; j < total_steps; j++){
pixels_inside++;
}
}
cout<<"Total pixel = "<<pixels_inside<<endl;
return 0;
}
首先您需要为 OpenMP 声明您正在使用的变量以及它们有什么保护。一般来说,您的代码具有 default(shared)
,因为您没有另外指定。这使得所有线程都可以使用相同的内存位置访问所有变量。
你应该使用这样的东西:
#pragma omp parallel default(none) shared(total_steps, pixels_inside)
[...]
#pragma omp task private(j) default(none) shared(total_steps, pixels_inside)
现在,线程只会使用必要的内容。
其次主要问题是你没有临界区保护。这意味着,当线程为 运行 时,它们可能希望使用共享变量并且发生竞争条件。例如,您有线程 A 和 B,两者都可以访问变量 x(a.k.a。共享内存变量)。现在假设 A 加 2,B 加 3 到变量。线程的速度不同,所以这可能会发生,A 取 x=0,B 取 x=0,A 加 0+2,B 加 0+3,B returns 数据到内存位置 x=3,A returns 数据到内存位置 x=2。最后 x = 2。pixels_inside
也会发生同样的情况,因为线程获取变量,将 1 和 returns 从它得到它的地方加回来。为了克服这个问题,您使用测量来确保关键部分保护:
#pragma omp critical
{
//Code with shared memory
pixels_inside++;
}
您在 reduction
中不需要临界区保护,因为 recution
参数中的变量具有此保护。
现在您的代码应该如下所示:
#include <iostream>
#include <omp.h>
using namespace std;
int main() {
int total_steps = 10000;
int i,j;
int pixels_inside=0;
omp_set_num_threads(4);
//#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
#pragma omp parallel default(none) shared(total_steps, pixels_inside)
#pragma omp single private(i)
for(i = 0; i < total_steps; i++){
#pragma omp task private(j) default(none) shared(total_steps, pixels_inside)
for(j = 0; j < total_steps; j++){
#pragma omp critical
{
pixels_inside++;
}
}
}
cout<<"Total pixel = "<<pixels_inside<<endl;
return 0;
}
尽管我建议使用 reduction
,因为它具有更好的性能和优化此类计算的方法。
正如@tartarus 已经解释的那样,您的代码中存在竞争条件,最好通过使用缩减来避免它。如果你想做和#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
一样的事情但使用任务,你必须使用以下内容:
#pragma omp parallel
#pragma omp single
#pragma omp taskloop reduction (+:pixels_inside) private(i,j)
for(i = 0; i < total_steps; i++){
for(j = 0; j < total_steps; j++){
pixels_inside++;
}
}
在这个版本中创建的任务更少,并且使用缩减而不是关键部分,因此性能会更好(类似于使用 #pragma omp parallel for
可以获得的性能)
更新(性能评论):我想这只是一个简化的示例,而不是您要并行化的真实代码。如果性能增益不够好,很可能意味着并行开销大于要做的工作。在这种情况下,请尝试并行化大部分代码。请注意,在任务的情况下,并行开销通常更大(与 #pragma omp parallel for
相比)。
我是 openMP 的新手。我正在尝试使用任务处理并行化嵌套循环,但它没有给我正确的计数器输出。顺序输出为“总像素 = 100000000”。有人可以帮我吗?
注意:我使用 #pragma omp parallel for reduction (+:pixels_inside) private(i,j) 完成了此操作。这很好用,现在我想使用任务。
到目前为止我尝试了什么:
#include<iostream>
#include<omp.h>
using namespace std;
int main(){
int total_steps = 10000;
int i,j;
int pixels_inside=0;
omp_set_num_threads(4);
//#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
#pragma omp parallel
#pragma omp single private(i)
for(i = 0; i < total_steps; i++){
#pragma omp task private(j)
for(j = 0; j < total_steps; j++){
pixels_inside++;
}
}
cout<<"Total pixel = "<<pixels_inside<<endl;
return 0;
}
首先您需要为 OpenMP 声明您正在使用的变量以及它们有什么保护。一般来说,您的代码具有 default(shared)
,因为您没有另外指定。这使得所有线程都可以使用相同的内存位置访问所有变量。
你应该使用这样的东西:
#pragma omp parallel default(none) shared(total_steps, pixels_inside)
[...]
#pragma omp task private(j) default(none) shared(total_steps, pixels_inside)
现在,线程只会使用必要的内容。
其次主要问题是你没有临界区保护。这意味着,当线程为 运行 时,它们可能希望使用共享变量并且发生竞争条件。例如,您有线程 A 和 B,两者都可以访问变量 x(a.k.a。共享内存变量)。现在假设 A 加 2,B 加 3 到变量。线程的速度不同,所以这可能会发生,A 取 x=0,B 取 x=0,A 加 0+2,B 加 0+3,B returns 数据到内存位置 x=3,A returns 数据到内存位置 x=2。最后 x = 2。pixels_inside
也会发生同样的情况,因为线程获取变量,将 1 和 returns 从它得到它的地方加回来。为了克服这个问题,您使用测量来确保关键部分保护:
#pragma omp critical
{
//Code with shared memory
pixels_inside++;
}
您在 reduction
中不需要临界区保护,因为 recution
参数中的变量具有此保护。
现在您的代码应该如下所示:
#include <iostream>
#include <omp.h>
using namespace std;
int main() {
int total_steps = 10000;
int i,j;
int pixels_inside=0;
omp_set_num_threads(4);
//#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
#pragma omp parallel default(none) shared(total_steps, pixels_inside)
#pragma omp single private(i)
for(i = 0; i < total_steps; i++){
#pragma omp task private(j) default(none) shared(total_steps, pixels_inside)
for(j = 0; j < total_steps; j++){
#pragma omp critical
{
pixels_inside++;
}
}
}
cout<<"Total pixel = "<<pixels_inside<<endl;
return 0;
}
尽管我建议使用 reduction
,因为它具有更好的性能和优化此类计算的方法。
正如@tartarus 已经解释的那样,您的代码中存在竞争条件,最好通过使用缩减来避免它。如果你想做和#pragma omp parallel for reduction (+:pixels_inside) private(i,j)
一样的事情但使用任务,你必须使用以下内容:
#pragma omp parallel
#pragma omp single
#pragma omp taskloop reduction (+:pixels_inside) private(i,j)
for(i = 0; i < total_steps; i++){
for(j = 0; j < total_steps; j++){
pixels_inside++;
}
}
在这个版本中创建的任务更少,并且使用缩减而不是关键部分,因此性能会更好(类似于使用 #pragma omp parallel for
可以获得的性能)
更新(性能评论):我想这只是一个简化的示例,而不是您要并行化的真实代码。如果性能增益不够好,很可能意味着并行开销大于要做的工作。在这种情况下,请尝试并行化大部分代码。请注意,在任务的情况下,并行开销通常更大(与 #pragma omp parallel for
相比)。