由于并发写入,在嵌套循环崩溃中使用 omp parallel?

Using omp parallel in a nested loop crash due to concurrent writing?

我正在尝试并行化我使用了一段时间的代码,而没有并行化,并且在一个相对较大的 C++ 项目(在 C++11 上进行的编译)中没有问题。该代码严重依赖 Eigen 包(此处使用 3.3.9 版)。 这是代码的简约版本,必须并行化并且我会为此崩溃(我希望在压缩东西时没有引入错误......):

主函数有两个需要并行化的for循环:

Data_eigensols solve(const VectorXd& nu_l0_in, const int el, const long double delta0l, 
    const long double DPl, const long double alpha, const long double q, const long double sigma_p, 
        const long double resol){

const int Nmmax=5000;

int np, ng, s, s0m;
double nu_p, nu_g;
VectorXi test;
VectorXd nu_p_all, nu_g_all, nu_m_all(Nmmax);
Data_coresolver sols_iter;
Data_eigensols nu_sols;

//----
// Some code that initialize several variables (that I do not show here for clarity).
// This includes initialising freq_max, freq_min, tol, nu_p_all, nu_g_all, deriv_p and deriv_g structures
//----

// Problematic loops that crash with omp parallel but works fine when not using omp
s0m=0;
#pragma omp parallel for collapse(2) num_threads(4) default(shared) private(np, ng)
        for (np=0; np<nu_p_all.size(); np++)
        {
                for (ng=0; ng<nu_g_all.size();ng++)
                {
                        nu_p=nu_p_all[np];
                        nu_g=nu_g_all[ng];
   
                        sols_iter=solver_mm(nu_p, nu_g); // Depends on several other function but for clarity, I do not show them here (all those 'const long double' or 'const int')
                        //printf("np = %d, ng= %d, threadId = %d \n", np, ng, omp_get_thread_num());
                        for (s=0;s<sols_iter.nu_m.size();s++)
                        {
                                // Cleaning doubles: Assuming exact matches or within a tolerance range
                                if ((sols_iter.nu_m[s] >= freq_min) && (sols_iter.nu_m[s] <= freq_max))
                                {
                                        test=where_dbl(nu_m_all, sols_iter.nu_m[s], tol, 0, s0m); // This function returns -1 if there is no match for the condition (here means that sols_iter.nu_m[s] is not found in nu_m_all)
                                        if (test[0] == -1) // Keep the solution only if it was not pre-existing in nu_m_all
                                        {
                                                nu_m_all[s0m]=sols_iter.nu_m[s];
                                                s0m=s0m+1;
                                        }
                                }
                        }              
                }
        }
    nu_m_all.conservativeResize(s0m); // Reduced the size of nu_m_all to its final size

    nu_sols.nu_m=nu_m_all;
    nu_sols.nu_p=nu_p_all;
    nu_sols.nu_g=nu_g_all;
    nu_sols.dnup=deriv_p.deriv; 
    nu_sols.dPg=deriv_g.deriv;

return, nu_sols;
}

类型Data_coresolver和Data_eigensols定义为:

struct Data_coresolver{
    VectorXd nu_m, ysol, nu,pnu, gnu; 
};

struct Data_eigensols{
    VectorXd nu_p, nu_g, nu_m, dnup, dPg;
};

where_dbl()如下:

VectorXi where_dbl(const VectorXd& vec, double value, const double tolerance){
/*
 * Gives the indexes of values of an array that match the value.
 * A tolerance parameter allows you to control how close the match
 * is considered as acceptable. The tolerance is in the same unit
 * as the value
 *
*/
   int cpt;
   VectorXi index_out;
  
   index_out.resize(vec.size());
    
    cpt=0;
    for(int i=0; i<vec.size(); i++){
        if(vec[i] > value - tolerance && vec[i] < value + tolerance){
            index_out[cpt]=i;
            cpt=cpt+1;
        }       
    }
    if(cpt >=1){
        index_out.conservativeResize(cpt);
    } else{
        index_out.resize(1);
        index_out[0]=-1;
    }
    return index_out;
}

关于 solver_mm(): 我没有详细说明这个函数,因为它调用的子程序很少,而且可能太长,无法在此处显示,而且我认为它与此处无关。它基本上是一个搜索求解隐式方程的函数。

主要功能应该做什么: 主函数 solve() 迭代调用 solver_mm() 以求解不同条件下的隐式方程,其中唯一的变量是 nu_p 和 nu_g。有时一对 (nu_p(i),nu_g(j)) 的解决方案与另一对 (nu_p(k), nu_g(l)) 导致重复的解决方案.这就是为什么有一个部分调用 where_dbl() 来检测那些重复的解决方案并抛出它们,只保留唯一的解决方案。

问题是什么: 没有#pragma 调用,代码工作正常。但是它在随机执行时失败了。 经过几次测试,罪魁祸首似乎与删除重复解决方案的部分有关。我的猜测是 nu_m_all VectorXd 上有并发写入。我尝试使用 #pragma omp barrier 但没有成功。但我对 omp 很陌生,我可能误解了屏障的工作原理。

谁能告诉我为什么我会在这里崩溃以及如何解决?对于一些在 omp 方面有丰富经验的人来说,解决方案可能是显而易见的。

nu_pnu_gsols_itertest 应该是私有的。由于这些变量被声明为共享变量,多个线程可能以非线程安全的方式写入同一内​​存区域。这可能是你的问题。