在QThread上使用时,OpenMP线程仅在一个CPU上运行。 (Qt,C ++)

时间:2016-09-24 15:11:41

标签: c++ multithreading qt openmp qthread

我正在尝试编写粒子模拟。 我创建了一个所有计算发生的工作类,并使用QThread将其移动到movetothread()以保持GUI响应(可行)。

现在为了加快计算,我想在worker中使用openMP。 奇怪的是,所有openMP线程似乎只在一个CPU核心上运行。 我注意到了这一点,因为无论是否使用openMP,CPU使用率(4个逻辑核心上约25%)和运行模拟的时间都不会改变。

在工作人员中调用omp_get_num_procs()时,它返回4但我没有观察到加速。我手动将num_threads()设置为4只是为了确定但它没有帮助。

程序编译并且计算正确(当使用openMP时也是如此)。

我听说同时使用QThread和openMP时可能会出现问题。显然,openMP生成的所有线程仅在分配给工作者移动到的QThread的CPU上运行。

我正在使用Windows 10 64位,Qt Creator 3.6.1,Qt 5.6.0(MSVC 2013,32位)。 在.pro文件中,我添加了QMAKE_CXXFLAGS += -fopenmpQMAKE_LFLAGS += -fopenmp。还LIBS += -fopenmp

qmake是:

  

qmake.exe ParticleSimulations.pro -r -spec win32-g ++“CONFIG + = debug”“CONFIG + = qml_debug”

Make is:

  

mingw32-make.exe在C:\ ...

编辑: 我还调用了'omp_get_num_threads()'返回4.我感兴趣的是线程是在单独的CPU上运行还是仅在一个上运行。

编辑: 根据请求这里是我的一些代码(不确定它是否有用) 这是来自mainwindow.cpp,其中创建了第二个负责计算的QThread:

    Pot=new LennardJones(sig,eps,mass,acc);
    if (ParticlesTypeID==0)
        Part=new TestParticles(confDia->getTestParticlesID(),L,Duration);
    else if (ParticlesTypeID==1)
        Part=new RandomParticles(n,L,Duration);
    else if (ParticlesTypeID==2)
        Part=new FileParticles(confDia->getValuesFromFile(),L,Duration);

    thread = new QThread;
    Pot->moveToThread(thread);

    connect(Pot, SIGNAL(finished()), thread, SLOT(quit()));
    connect(Pot, SIGNAL(finished()), Pot, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(this, SIGNAL(CancelClick()), Pot, SLOT(onCancelClick()));
    connect(Pot, SIGNAL(sendProgress(int)), this, SLOT(on_Progress(int)));
    connect(Pot, SIGNAL(sendTime(double)), this, SLOT(on_Time(double)));

    thread->start();
    // Calculation
        Part->setParticleData(Pot->Newton(Part->getParticleData(),Part->gettspan(),Part->getL(),MaxNewtonIterations,MaxSteps,MinSteps));

这是计算的一部分(仅作为示例)。调用omp_get_num_threads()或omp_get_num_procs()使我确信OpenMP原则上正在工作。

void LennardJones::NewtonIteration(Eigen::MatrixXd &ParticleData, Eigen::VectorXd &FirstDer, Eigen::MatrixXd &SecDer, Eigen::VectorXd &GKplus1, Eigen::MatrixXd &GderInvKplus1, Eigen::MatrixXd &temp, int &k, double &deltat, int &maxiterations){
int counter=0;
while(((temp-ParticleData.col(k)).norm() > accuracy) && (counter<maxiterations)){
    #pragma omp parallel sections
    {
        #pragma omp section
        {
            FirstDer=FirstDerivative(ParticleData);
            GKplus1=BuildGKplus1(ParticleData,FirstDer,deltat); 
        }
        #pragma omp section
        {
            SecDer=SecondDerivative(ParticleData);
            GderInvKplus1=BuildGderInvKplus1(ParticleData,SecDer,deltat);              
        }

    }       
    temp = ParticleData.col(k);
    ParticleData.col(k)=ParticleData.col(k)-GderInvKplus1*GKplus1;
    counter++;
}

}

另一个代码snipplet。在这里,我尝试并行化一个运行n次的for循环(n是粒子的数量,因此应该有很大的性能提升空间)

#pragma omp parallel for
for (int i=1;i<=n;i++){
    for (int j=1;j<=n;j++){
        if(j!=i){
            //Positionsvektoren die in diesem Schritt betrachtet werden
            v1K=ParticleData.block(3*(n+i-1),ParticleData.cols()-2,3,1);
            v2K=ParticleData.block(3*(n+j-1),ParticleData.cols()-2,3,1);
            v1Kplus1=ParticleData.block(3*(n+i-1),ParticleData.cols()-1,3,1);
            v2Kplus1=ParticleData.block(3*(n+j-1),ParticleData.cols()-1,3,1);

            //Distanz r
            rK = (v1K-v2K).norm(); //Distance(v1K,v2K);
            rKplus1 = (v1Kplus1-v2Kplus1).norm();   //Distance(v1Kplus1,v2Kplus1);

            //Großer ausklammerbarer Term in der ersten Ableitung
            tempK=-12*epsilon*(pow(sigma,12)/pow(rK,14) - pow(sigma,6)/pow(rK,8));
            tempKplus1=-12*epsilon*(pow(sigma,12)/pow(rKplus1,14) - pow(sigma,6)/pow(rKplus1,8));

            // Erste Ableitungen
            FirstDerK.segment(3*i-3,3)+=tempK*(v1K-v2K);
            FirstDerKplus1.segment(3*i-3,3)+=tempKplus1*(v1Kplus1-v2Kplus1);


        }
    }
}

提前多多感谢! :)

1 个答案:

答案 0 :(得分:0)

我将OpenMP与QThread一起使用,它们可以正常工作。与问题中描述的情况的两个主要差异是:

1)我正在使用Linux和GCC6

2)worker类和QThread类是分开的。通过这个,我的意思是有一个纯粹的STL / Eigen /无论如何,但是没有Qt的工人阶级。然后,有一个继承自QObject的包装类,它拥有一个worker类的实例。信号槽连接是在应用程序和包装类之间建立的,而后者又调用了worker类的方法。

通过这样的设置,并发度等于核心数。

为了进一步调试并发问题,我建议下载免费的Intel Parallel Studio XE并执行配置文件运行。然后,您将能够观察到(a)并发度和(b)是否正在使用OpenMP,因为当它出现时,您可以在配置文件中看到_ omp函数。

这样,我看到2个线程(主GUI + QThread一个),然后在调用OpenMP启用的函数时创建了12个(Xeon X5650 HT on)。因此,我得出结论,QThread线程与OpenMP线程分开。顺便说一下,我使用纯粹的GDB调试器进行观察,因为每次创建新线程时它都会输出一条消息。

除此之外,我看到建议使用一组不同的标志来使用OpenMP:-fopenmp-lgomp。所以,在你的情况下,

QMAKE_CXXFLAGS += -fopenmp
QMAKE_LFLAGS += -lgomp

虽然在GCC5和GCC6上,只有第一个标志-fopenmp就够了。