Qt MinGW QThread 性能不好
Qt MinGW QThread performance bad
我们目前正在使用 OpenGL 开发一个简单的 3D 引擎,几乎一切都已准备好用于我们最终的大学演示。我通常使用 MSVC2015 x64 编译器,一切正常,构建速度很快,可执行性能也很好。
然而,由于我们所做的最新更改,物理线程在 MinGW 4.9.2 x86 上非常慢,并且一些变量如 delta 奇怪地为 0,即使线程正在工作。
当我切换回 MSVC 时,它就像一种魅力一样难以描述,所以我深表歉意。
为了便于理解,这里有一张 GIF:http://imgur.com/Isgqkcz
如您所见,球体的移动非常不稳定。在 MSVC 上,它们移动得非常平滑,增量通常为 0.007831ms。在 MinGW 上,delta 为 0ms,有时,有时非常高,如 ~5ms。我们完全不知道它是什么原因造成的。我怀疑编译器优化了什么?我不知道...
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;
}
QThread
和mingw都没有问题,是你误诊了。请记住,QThread
是本机线程的一个非常简单的句柄。这与您所看到的无关。
您的时间步长逻辑存在根本性缺陷。您假设 startTime
和 endTime
会有所不同。 他们不必这样做 - 您刚刚为自己制作了一个测试用例来证明这一点。您正在使用的时钟 API 中没有任何东西会保证当你调用它两次时你会得到不同的结果。
您的物理引擎应该以恒定的时间步长运行,并且在每个时间步长上添加一个内部时间。当您开始仿真时,将引擎的时间设置为系统时间。然后只要滞后于实时,就一直在引擎中计算步数。一旦赶上,执行显示更新。并重复。
我们目前正在使用 OpenGL 开发一个简单的 3D 引擎,几乎一切都已准备好用于我们最终的大学演示。我通常使用 MSVC2015 x64 编译器,一切正常,构建速度很快,可执行性能也很好。 然而,由于我们所做的最新更改,物理线程在 MinGW 4.9.2 x86 上非常慢,并且一些变量如 delta 奇怪地为 0,即使线程正在工作。
当我切换回 MSVC 时,它就像一种魅力一样难以描述,所以我深表歉意。
为了便于理解,这里有一张 GIF:http://imgur.com/Isgqkcz
如您所见,球体的移动非常不稳定。在 MSVC 上,它们移动得非常平滑,增量通常为 0.007831ms。在 MinGW 上,delta 为 0ms,有时,有时非常高,如 ~5ms。我们完全不知道它是什么原因造成的。我怀疑编译器优化了什么?我不知道...
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;
}
QThread
和mingw都没有问题,是你误诊了。请记住,QThread
是本机线程的一个非常简单的句柄。这与您所看到的无关。
您的时间步长逻辑存在根本性缺陷。您假设 startTime
和 endTime
会有所不同。 他们不必这样做 - 您刚刚为自己制作了一个测试用例来证明这一点。您正在使用的时钟 API 中没有任何东西会保证当你调用它两次时你会得到不同的结果。
您的物理引擎应该以恒定的时间步长运行,并且在每个时间步长上添加一个内部时间。当您开始仿真时,将引擎的时间设置为系统时间。然后只要滞后于实时,就一直在引擎中计算步数。一旦赶上,执行显示更新。并重复。