Qt MinGW QThread 性能不佳

Posted

技术标签:

【中文标题】Qt MinGW QThread 性能不佳【英文标题】:Qt MinGW QThread performance bad 【发布时间】:2016-06-28 00:07:09 【问题描述】:

我们目前正在使用 OpenGL 开发一个简单的 3D 引擎,几乎一切都已为我们的期末大学演示做好了准备。我通常使用 MSVC2015 x64 编译器,一切正常,构建速度快,可执行性能也好。 然而,由于我们进行了最新的更改,MinGW 4.9.2 x86 上的物理线程非常慢,并且一些变量(如 delta)奇怪地为 0,即使线程正在工作。

当我切换回 MSVC 时,它就像一个魅力难以描述,所以我很抱歉。

这里有一个 GIF 以便更好地理解:http://imgur.com/Isgqkcz

如您所见,球体的移动非常不稳定。在 MSVC 上,它们的移动非常平滑,增量通常为 0.007831 毫秒。在 MinGW 上,增量为 0 毫秒,有时,有时它非常高,例如 ~ 5 毫秒。我们完全不知道它是什么原因造成的。我怀疑编译器优化了一些东西?我不知道...

void PhysicsThread::run()
    qDebug() << "SUCCESSFULLY STARTED UP PHYSICS-SIMULATION";
    forever
        mutex.lock();
        qDebug() << "RUNNING PHYSICS-SIMULATION";
        runSimulation();
        if(stop)
            mutex.unlock();
            break;
        
        if(bPause)
            pauseManager.wait(&mutex);
        
        mutex.unlock();
    


void PhysicsThread::runSimulation()
    auto startTime = std::chrono::high_resolution_clock::now();

    // Collision Border
    for(int i = 0 ; i < pobjectsSphere.size() ; i++) 

        PhysicsSphere* op = pobjectsSphere.at(i);
        if(op->getIsMovable())
            if(op->getX()-(op->getSize()) < minx )
                if(op->getVelocityX() < 0)
                    op->setVelocityX(-op->getVelocityX());
                else
                    op->setVelocityX(op->getVelocityX());
                
            else if(op->getX()+(op->getSize()) > maxx) 
                if(op->getVelocityX() > 0)
                    op->setVelocityX(-op->getVelocityX());
                else
                    op->setVelocityX(op->getVelocityX());
                
            

            if(op->getY()-(op->getSize()) < miny)
                if(op->getVelocityY() < 0)
                    op->setVelocityY(-op->getVelocityY() * op->getRemainingEnergy());
                else
                    op->setVelocityY(op->getVelocityY());
                
            else
                if(op->getY()+(op->getSize()) > maxy)
                    if(op->getVelocityY() > 0)
                        op->setVelocityY(-op->getVelocityY());
                    else
                        op->setVelocityY(op->getVelocityY());
                    
                

                // Gravity
                op->setVelocityY(op->getVelocityY() + g*deltaTimeMS*op->getMass());
            

            if(op->getZ()-(op->getSize()) < minz)
                if(op->getVelocityZ() < 0)
                    op->setVelocityZ(-op->getVelocityZ());
                else
                    op->setVelocityZ(op->getVelocityZ());
                
            else if(op->getZ()+(op->getSize()) > maxz)
                if(op->getVelocityZ() > 0)
                    op->setVelocityZ(-op->getVelocityZ());
                else
                    op->setVelocityZ(op->getVelocityZ());
                
            
        
    

    // Collision Sphere on Sphere
    for(int i = 0 ; i < pobjectsSphere.size() ; i++) 
        PhysicsSphere* op1 = pobjectsSphere.at(i);
        for(int j = i ; j < pobjectsSphere.size() ; j++) 
            PhysicsSphere* op2 = pobjectsSphere.at(j);

            // Sphere on Sphere
            if(i != j && Collision::SphereVersusSphere(op1->getX() ,op1->getY() ,op1->getZ() ,op1->getSize() ,op2->getX() ,op2->getY() ,op2->getZ() ,op2->getSize()))

                double tempX (op1->getX() - op2->getX());
                double tempY (op1->getY() - op2->getY());
                double tempZ (op1->getZ() - op2->getZ());

                double norm = sqrt(tempX*tempX + tempY*tempY + tempZ*tempZ);

                tempX = tempX / norm;
                tempY = tempY / norm;
                tempZ = tempZ / norm;

                double a1 = (op1->getVelocityX() * tempX) + (op1->getVelocityY() * tempY) + (op1->getVelocityZ() * tempZ);
                double a2 = (op2->getVelocityX() * tempX) + (op2->getVelocityY() * tempY) + (op2->getVelocityZ() * tempZ);

                double optimizedP = (2.0 * (a1 - a2)) / (op1->getMass() + op2->getMass());

                // fix
                optimizedP = std::abs(optimizedP);

                // 0.9 Verlusst
                if(op1->getIsMovable())
                    op1->setVelocityX( op1->getVelocityX() + (optimizedP * op2->getMass() * tempX) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                    op1->setVelocityY( op1->getVelocityY() + (optimizedP * op2->getMass() * tempY) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                    op1->setVelocityZ( op1->getVelocityZ() + (optimizedP * op2->getMass() * tempZ) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                

                if(op2->getIsMovable())
                    op2->setVelocityX( op2->getVelocityX() - (optimizedP * op1->getMass() * tempX) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                    op2->setVelocityY( op2->getVelocityY() - (optimizedP * op1->getMass() * tempY) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                    op2->setVelocityZ( op2->getVelocityZ() - (optimizedP * op1->getMass() * tempZ) * (op1->getRemainingEnergy()*op2->getRemainingEnergy()));
                

                if(!op1->getIsMovable() && op2->getIsMovable())
                    op2->setX(op2->getX() - op1->getVelocityX() * deltaTimeMS);
                    op2->setY(op2->getY() - op1->getVelocityY() * deltaTimeMS);
                    op2->setZ(op2->getZ() - op1->getVelocityZ() * deltaTimeMS);

                    op1->setVelocityX(0.0);
                    op1->setVelocityY(0.0);
                    op1->setVelocityZ(0.0);

                else if(op1->getIsMovable() && !op2->getIsMovable())
                    op1->setX(op1->getX() - op2->getVelocityX() * deltaTimeMS);
                    op1->setY(op1->getY() - op2->getVelocityY() * deltaTimeMS);
                    op1->setZ(op1->getZ() - op2->getVelocityZ() * deltaTimeMS);

                    op2->setVelocityX(0.0);
                    op2->setVelocityY(0.0);
                    op2->setVelocityZ(0.0);
                


                op1->setX(op1->getX() + op1->getVelocityX() * deltaTimeMS);
                op1->setY(op1->getY() + op1->getVelocityY() * deltaTimeMS);
                op1->setZ(op1->getZ() + op1->getVelocityZ() * deltaTimeMS);

                op2->setX(op2->getX() + op2->getVelocityX() * deltaTimeMS);
                op2->setY(op2->getY() + op2->getVelocityY() * deltaTimeMS);
                op2->setZ(op2->getZ() + op2->getVelocityZ() * deltaTimeMS);
            
        
    

    for(int i = 0 ; i < pobjectsSphere.size() ; i++) 
        PhysicsSphere* op1 = pobjectsSphere.at(i);
        for(int j = 0 ; j < pobjectsBox.size() ; j++) 
            PhysicsBox* op2 = pobjectsBox.at(j);



            if(Collision::SphereVersusBox( op1->getX() ,op1->getY() ,op1->getZ() ,op1->getSize() ,op2->getMinX()+op2->getX() ,op2->getMinY()+op2->getY() ,op2->getMinZ()+op2->getZ() ,op2->getMaxX()+op2->getX() ,op2->getMaxY()+op2->getY() ,op2->getMaxZ()+op2->getZ()))

                if((op1->getX()+op1->getSize()) > op2->getMinX() && op1->getX() < op2->getMinX()+op2->getX())

                    if(op1->getVelocityX() > 0)
                        op1->setVelocityX(-op1->getVelocityX());
                    
                
                if((op1->getX()-op1->getSize()) < op2->getMaxX() && op1->getX() > op2->getMaxX()+op2->getX())
                    if(op1->getVelocityX() < 0)
                        op1->setVelocityX(-op1->getVelocityX());
                    
                

                if((op1->getY()+op1->getSize()) > op2->getMinY() && op1->getY() < op2->getMinY()+op2->getY())
                    if(op1->getVelocityY() > 0)
                        op1->setVelocityY(-op1->getVelocityY());
                    
                
                if((op1->getY()-op1->getSize()) < op2->getMaxY() && op1->getY() > op2->getMaxY()+op2->getY())

                    if(op1->getVelocityY() < 0)
                        op1->setVelocityY(-op1->getVelocityY());
                    
                

                if((op1->getZ()+op1->getSize()) > op2->getMinZ() && op1->getZ() < op2->getMinZ()+op2->getZ())
                    if(op1->getVelocityZ() > 0)
                        op1->setVelocityZ(-op1->getVelocityZ());
                    
                
                if((op1->getZ()-op1->getSize()) < op2->getMaxZ() && op1->getZ() > op2->getMaxZ()+op2->getZ())
                    if(op1->getVelocityZ() < 0)
                        op1->setVelocityZ(-op1->getVelocityZ());
                    
                
            
        
    

    // Move
    for(int i = 0 ; i < pobjectsSphere.size() ; i++) 
        PhysicsSphere* op = pobjectsSphere.at(i);
        if(op->getIsMovable())
            op->setX(op->getX() + op->getVelocityX()*deltaTimeMS);
            op->setY(op->getY() + op->getVelocityY()*deltaTimeMS);
            op->setZ(op->getZ() + op->getVelocityZ()*deltaTimeMS);
        else
            op->setVelocityX(0.0);
            op->setVelocityY(0.0);
            op->setVelocityZ(0.0);
        

    
    if(pauseTickTime > 0.0)
        this->msleep(pauseTickTime);
    
    auto endTime = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time = endTime - startTime;
    deltaTimeNS = std::chrono::duration_cast<std::chrono::nanoseconds>(time).count();
    deltaTimeMS = deltaTimeNS / 1000000.0;
    //qDebug() << "DeltaT NS: " << deltaTimeNS << " DeltaT MS: " << deltaTimeMS;

【问题讨论】:

【参考方案1】:

QThread 或 mingw 都没有问题,你误诊了问题。请记住,QThread 是一个非常简单的本地线程句柄。这与您所看到的无关。

您的时间步逻辑存在根本缺陷。您假设 startTimeendTime 会有所不同。 他们不必这样做 - 您刚刚为自己制作了一个测试用例来证明这一点。您使用的时钟 API 中没有任何内容可以保证您会得到结果当你调用它两次时会有所不同。

您的物理引擎应该以恒定的时间步长运行,并且在每个时间步长上添加一个内部时间。开始模拟时,将引擎时间设置为系统时间。然后继续计算引擎中的步骤,只要它落后于实时。一旦赶上,执行显示更新。然后重复。

【讨论】:

我假设您指的是 Glenn Fiedlers 的文章“释放物理学”?实际上,我找到了导致我的 delta 为 0 的罪魁祸首。ostd::chrono::high_resolution_clock 可以是 system_clock 的同义词。 MinGW 编译器似乎就是这种情况,因此精度要低得多,导致增量不改变。无论如何感谢您的提示,我会检查物理文章并重构物理代码! @KevinKuegler 同样,这些都不重要。您关于获取时钟值的两次调用将不同的假设是根本错误。更糟糕的是,这甚至不必是单调时钟,因此您的时钟可以倒退 - 通常在上下文切换时。 @KevinKuegler 此外,如果您的任务由于某种原因被延迟,您可能会得到非常长的时间增量,并且会使模拟步骤的结果无用。我不是指任何人的文章,我只是指您的实现在一个非常基本的层面上被破坏的事实。不要与之抗争。做对了。这会容易得多。 @KevinKuegler 您可以选择在引擎因任何原因落后太远时重置引擎中的参考时间。 嗯好吧抱歉,我误解了你的回答。我将尝试理解一切并重做逻辑。当我完成后,我会发布结果以供参考,所以谢谢!

以上是关于Qt MinGW QThread 性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

Qt系列文章之二十八(基于QThread多线程概述)

从继承的 QThread 迁移到 Worker 模型

QT之深入理解QThread

C++/Qt - QThread 与 QRunnable

如何在 Qt c++ 中管理 QThread?

Qt:将事件发布到 QThread 的正确方法?