openmp:检查是否嵌套并行

openmp : check if nested parallesim

假设我有一个方法将两个 std::vector 相乘:

double multiply(std::vector<double> const& a, std::vector<double> const& b){
    double tmp(0);
    /*here I could easily do a parallelization with*/
    /*#pragma omp parallel loop for*/
    for(unsigned int i=0;i<a.size();i++){
        tmp += a[i]*b[i];
    }
    return tmp;
}

如果我在此函数中设置 pragma 宏,对 multiply(...) 的调用将 运行 在所有线程上。

现在假设我想在其他地方做很多向量乘法:

void many_multiplication(std::vector<double>* a, std::vector<double>* b, unsigned int N){
    /*here I could easily do a parallelization with*/
    /*#pragma omp parallel loop for*/
    for(unsigned int i=0;i<N;i++){
        for(unsigned int j=0;j<N;j++){
            multiply(a[i],b[j]);
        }
    }
}

我也可以用同样的方式进行并行化。但这会导致 不需要的嵌套并行性。

如何检查 multiply(..) 是否在并行区域内调用, 那么 multiply(...)pragma 宏就是 "turn off"。如果它被称为 来自非平行区域,则为 "turn on".

将 pragma 添加到两个函数。您可以使用 omp_set_nested(int val) 打开和关闭 nested parallelism(零表示关闭,非零表示打开)。

因此,如果您通常希望在您的程序中启用嵌套并行性,但对于 many_multiplication 函数则关闭,您可以按如下方式实现 many_multiplication

void many_multiplication(std::vector<double>* a, std::vector<double>* b, unsigned int N){
    omp_set_nested(0);
    #pragma omp parallel loop for
    for(unsigned int i=0;i<N;i++){
        for(unsigned int j=0;j<N;j++){
            multiply(a[i],b[j]);
        }
    }
    omp_set_nested(1);
}

嵌套并行性默认情况下 禁用 ,除非通过将 OMP_NESTED 设置为 true 或调用 omp_set_nested(1);(§2.3. OpenMP specification 中的 2 个)按照 Avi Ginsburg 的建议明确修改嵌套设置是个坏主意。相反,您应该使用基于嵌套级别的条件并行执行:

double multiply(std::vector<double> const& a, std::vector<double> const& b){
    double tmp(0);
    int active_levels = omp_get_active_level();
    #pragma omp parallel for reduction(+:tmp) if(active_level < 1)
    for(unsigned int i=0;i<a.size();i++){
        tmp += a[i]+b[i];
    }
    return tmp;
}

omp_get_active_level() returns 在进行调用时包围线程的活动并行区域的数量。它 returns 0 如果从平行区域外部或不活动的外部区域调用。由于 if(active_level < 1) 子句,平行区域将仅被激活,即 运行 平行,如果它不包含在活动区域​​中,无论嵌套设置如何。

如果您的编译器不支持 OpenMP 3.0 或更高版本(例如,任何版本的 MS Visual C/C++ 编译器),则可以使用 omp_in_parallel() 调用:

double multiply(std::vector<double> const& a, std::vector<double> const& b){
    double tmp(0);
    int in_parallel = omp_in_parallel();
    #pragma omp parallel for reduction(+:tmp) if(in_parallel == 0)
    for(unsigned int i=0;i<a.size();i++){
        tmp += a[i]+b[i];
    }
    return tmp;
}

omp_in_parallel() returns 如果至少有一个封闭的平行区域处于活动状态,则非零,但不提供有关嵌套深度的信息,即不太灵活。

无论如何,写这样的代码是一种不好的做法。您应该简单地保留并行区域,让最终用户选择是否启用嵌套并行。