在Windows(Mingw64)上可执行速度比Linux慢3倍

时间:2018-11-06 19:12:30

标签: c++ mingw-w64

我正在开发一个游戏引擎,该引擎在Windows上比Linux上慢3-4倍。 我尝试对应用程序进行概要分析,除了在Windows上一切似乎都变慢之外,我没有看到任何特殊问题。 我提取了应用程序的一小部分。我用g++ -O3 perf.cpp编译它,并执行如下:a.exe 500000000。结果如下:

  • Linux:10秒(平均5次执行):g ++ 8.2 [也已通过g ++ 7.3进行了测试]
  • Windows:27秒(平均执行5次):g ++(x86_64-posix-seh-rev0,由MinGW-W64项目构建)8.1.0 [还通过g ++ 7.1进行了测试]

源代码:

#include <iostream>
#include <cmath>
#include <vector>
#include <chrono>

struct Vector{
    float X, Y, Z;

    Vector(float X, float Y, float Z) : X(X), Y(Y), Z(Z){}

    Vector vector(const Vector &target) const{
        return Vector(target.X - X, target.Y - Y, target.Z - Z);
    }

    float dotProduct(const Vector &v) const{
        return (X*v.X + Y*v.Y + Z*v.Z);
    }
};

float compute(const std::vector<Vector> &v){
    Vector vec1 = v[0].vector(v[2]);
    Vector vec2 = v[1].vector(v[0]);
    return vec1.dotProduct(vec2);
}

int main(int argc, char *argv[]){
    unsigned int loopMax = atoi(argv[1]);

    Vector va(1.5f, 3.0f, 8.0f*loopMax);
    Vector vb(1.2f, 2.3f, 11.0f*loopMax);
    Vector vc(8.2f, 5.0f, 12.0f*loopMax);

    auto frameStartTime = std::chrono::high_resolution_clock::now();

    float res = 0.0f;
    for(unsigned int i=0; i<loopMax; ++i)
    {       
        res += compute({va, vb, vc});
    }

    auto frameEndTime = std::chrono::high_resolution_clock::now();
    auto diffTimeMicroSeconds = std::chrono::duration_cast<std::chrono::microseconds>(frameEndTime - frameStartTime).count();

    std::cout<<"Time: "<<diffTimeMicroSeconds / 1000000.0 <<" sec, res: "<<res<<std::endl;
    return 0;
}

我知道一次迭代的差异是荒谬的(<1 us),但结果是我的应用程序慢了3-4倍。

什么可以解释这种差异?如何找到问题?

1 个答案:

答案 0 :(得分:3)

尝试在循环外构造compute的参数。如果编译器没有取消std::vector<Vector>参数的构造,则很可能会导致堆分配:

std::vector<Vector> arg{va, vb, vc};
for(unsigned int i=0; i<loopMax; ++i)
{       
    res += compute(arg);
}

如果有堆分配,则很可能比循环内容的其余部分花费更多的时间。堆分配需要多长时间,具体取决于系统和实现。在这两种情况下,删除它都可能会显着提高性能。

如果每次都需要在实际代码中构造向量,则应考虑使用固定大小的数组(原始数组或std :: array),该数组不会在堆上分配,而是在堆栈上分配,这快得多。这似乎适用,因为您恰好在compute实现中使用了三个元素。

如果您不知道编译时向量的长度 ,并且每次都需要在热循环中对其进行重构,那么您也许可以重用堆-分配的空间,如果您可以为最大长度指定一个很好的猜测,则更是如此:

std::vector<Vector> arg;
arg.reserve(1000); // Allocate for up to 1000 element
for(unsigned int i=0; i<loopMax; ++i)
{   
    arg.clear();
    arg.push_back(va);
    [...]
    arg.push_back(vn);
    res += compute(arg);
}