我正在尝试编写粒子模拟。
我创建了一个所有计算发生的工作类,并使用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 += -fopenmp
和QMAKE_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);
}
}
}
提前多多感谢! :)
答案 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
就够了。