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

OpenMP threads only run on one CPU when used on a QThread. (Qt, C++)

本文关键字:一个 CPU C++ Qt 运行 QThread 线程 OpenMP      更新时间:2023-10-16

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

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

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

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

我听说同时使用QThreads和openMP时可能会出现问题。显然,openMP派生的所有线程都只在分配给工作线程的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">

品牌:

C:\中的mingw32-make.exe。。。

编辑:我还调用了"omp_get_num_threads((",返回4。我感兴趣的是线程是在单独的CPU上运行还是只在一个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++;
}

}

另一个代码片段。在这里,我尝试将运行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);

        }
    }
}

提前感谢!:(

我将OpenMP与QThread一起使用,它们可以正确地协同工作。与问题中描述的情况的两个主要区别是:

1( 我正在使用带有GCC6 的Linux

2( 工人类和QThread类是分开的。我的意思是,有一个纯粹的STL/Eigen/任何东西,但没有Qt,工人类。然后,有一个包装器类,它继承自QObject,并拥有工作类的一个实例。信号槽连接是在应用程序和包装类之间建立的,包装类反过来调用工作类的方法。

有了这样的设置,并发度等于核心数。

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

通过这种方式,我看到在调用启用OpenMP的函数时创建了2个线程(主GUI+QThread一个(,然后又创建了12个线程(Xeon X5650 HT on(。因此,我得出结论,QThread线程与OpenMP线程是分开的。顺便说一句,我设法用一个GDB调试器来观察它,因为每次创建新线程时,它都会输出一条消息。

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

QMAKE_CXXFLAGS += -fopenmp
QMAKE_LFLAGS += -lgomp

尽管在GCC5和GCC6上,仅仅第一个标志-fopenmp就足够了。