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 相比)。