2015-08-28 78 views
2

我想了解为什么使用-O2 -march = native与GCC给出比不使用它们慢的代码。 请注意,我的Windows 7“坏”GCC优化性能

这里下使用的MinGW(GCC 4.7.1)是我的代码:

struct.hpp:

#ifndef STRUCT_HPP 
#define STRUCT_HPP 

#include <iostream> 

class Figure 
{ 
public: 
    Figure(char *pName); 
    virtual ~Figure(); 

    char *GetName(); 
    double GetArea_mm2(int factor); 

private: 
    char name[64]; 
    virtual double GetAreaEx_mm2() = 0; 
}; 

class Disk : public Figure 
{ 
public: 
    Disk(char *pName, double radius_mm); 
    ~Disk(); 

private: 
    double radius_mm; 
    virtual double GetAreaEx_mm2(); 
}; 

class Square : public Figure 
{ 
public: 
    Square(char *pName, double side_mm); 
    ~Square(); 

private: 
    double side_mm; 
    virtual double GetAreaEx_mm2(); 
}; 

#endif 

struct.cpp:

#include <cstdio> 
#include "struct.hpp" 

Figure::Figure(char *pName) 
{ 
    sprintf(name, pName); 
} 

Figure::~Figure() 
{ 
} 

char *Figure::GetName() 
{ 
    return name; 
} 

double Figure::GetArea_mm2(int factor) 
{ 
    return (double)factor*GetAreaEx_mm2(); 
} 

Disk::Disk(char *pName, double radius_mm_) : 
Figure(pName), radius_mm(radius_mm_) 
{ 
} 

Disk::~Disk() 
{ 
} 

double Disk::GetAreaEx_mm2() 
{ 
    return 3.1415926*radius_mm*radius_mm; 
} 

Square::Square(char *pName, double side_mm_) : 
Figure(pName), side_mm(side_mm_) 
{ 
} 

Square::~Square() 
{ 
} 

double Square::GetAreaEx_mm2() 
{ 
    return side_mm*side_mm; 
} 

main.cpp

#include <iostream> 
#include <cstdio> 
#include "struct.hpp" 

double Do(int n) 
{ 
    double sum_mm2 = 0.0; 
    const int figuresCount = 10000; 
    Figure **pFigures = new Figure*[figuresCount]; 

    for (int i = 0; i < figuresCount; ++i) 
    { 
     if (i % 2) 
      pFigures[i] = new Disk((char *)"-Disque", i); 
     else 
      pFigures[i] = new Square((char *)"-Carré", i); 
    } 

    for (int a = 0; a < n; ++a) 
    { 
     for (int i = 0; i < figuresCount; ++i) 
     { 
      sum_mm2 += pFigures[i]->GetArea_mm2(i); 
      sum_mm2 += (double)(pFigures[i]->GetName()[0] - '-'); 
     } 
    } 

    for (int i = 0; i < figuresCount; ++i) 
     delete pFigures[i]; 

    delete[] pFigures; 

    return sum_mm2; 
} 

int main() 
{ 
    double a = 0; 

    StartChrono();  // home made lib, working fine 
    a = Do(10000); 
    double elapsedTime_ms = StopChrono(); 

    std::cout << "Elapsed time : " << elapsedTime_ms << " ms" << std::endl; 

    return (int)a % 2; // To force the optimizer to keep the Do() call 
} 

予两次编译该代码:

1:无优化

的mingw32-G ++ EXE -Wall -fexceptions -std = C++ 11 -c的main.cpp -o主。 ø

的mingw32-G ++。EXE -Wall -fexceptions -std = C++ 11 -c struct.cpp -o struct.o

的mingw32-G ++。EXE -o的Program.exe main.o结构。 o -s

2:和-O2优化

的mingw32-G ++ EXE -Wall -fexceptions -O2 -march =天然-std = C++ 11 -c的main.cpp -o main.o

的mingw32 -g ++ .exe -Wall -fexceptions -O2 -march = native -std = C++ 11 -c struct.cpp -o struct.o

mingw32-g ++。exe -o program.exe main.o struct。 ø-s

1:执行时间:

1196毫秒(毫秒1269与Visual Studio 2013)

2:执行时间:

1569毫秒(403毫秒与Visual Studio 2013)!!!!!!!!!!!!!

使用-O3代替-O2不会改善结果。 我当时还是很确信GCC和Visual Studio是等价的,所以我不明白这个巨大的差别。 另外,我不明白为什么优化后的版本比使用GCC的非优化版本要慢。

我想念这里的东西吗? (请注意,我有与真正的GCC 4.8相同的问题。2在Ubuntu)

感谢您的帮助

+0

[OT]:在构造函数中使用const char *而不是将'const char *'强制转换为'char *'。 – Jarod42

+0

'sprintf(name,pName);'很危险...... – Jarod42

+0

谢谢,我完全意识到这一点,因此,没有任何借口:) –

回答

2

考虑到我没有看到汇编代码,我要去猜测如下:

分配回路可以优化(由编译器)通过去除如果子句和导致以下:

for (int i=0;i <10000 ; i+=2) 
{ 
     pFigures[i] = new Square(...); 
} 
for (int i=1;i <10000 ; i +=2) 
{ 
     pFigures[i] = new Disk(...); 
} 

考虑到结束条件是4的倍数,也可以是更“有效”的

for (int i=0;i < 10000 ;i+=2*4) 
{ 
    pFigures[i] = ... 
    pFigures[i+2] = ... 
    pFigures[i+4] = ... 
    pFigures[i+6] = ... 
} 

记忆明智的是,这将使磁盘分配4乘4 4乘4。

现在,这意味着它们将在内存中彼此相邻。

接下来,您将以正常顺序迭代矢量10000次(通过我的意思是索引后的索引)。

想想这些形状在内存中的分配位置。您最终会有4倍以上的缓存未命中(想想边界示例,当在不同页面中找到4个磁盘和4个方块时,您将在第8页......在正常情况下,您只需在页面之间切换一次)。

这种优化(如果由编译器和您的特定代码完成)优化了分配时间,但不是访问时间(在您的示例中是最大负载)。

通过删除i%2并查看得到的结果来测试此操作。

这又是纯粹的推测,它假定性能较低的原因是循环优化。

+0

感谢您的回答,这是一个完全可能的解释。但奇怪的是,相反,Visual Studio设法大幅划分执行时间,不是吗?另外,这是一个相当简单的情况,VS比GCC好得多。仍然在这里猜测。 –

+0

您的建议结果(我只分配磁盘,没有条件在循环了): 非优化:1258毫秒 优化:675毫秒(仍然超过VisualStudio的条件...) 似乎你的猜测可能是正确的,但仍然不能解释为什么VS做到这一点 –

+0

也许vs移动for(int a ...)循环里面,所以而不是(a){for(i){}}你得到一个for(i){for(a){}},它应该更快。尝试自己移动循环,看看结果是否接近。我都没有其他推测:)。 – MichaelCMS

1

我怀疑你有一个Windows上mingw/gcc/glibc的组合特有的问题,因为你的代码在Linux上优化的时候执行得更快,而gcc在这里更加“在家”。

在一个相当行人的Linux VM使用gcc 4.8.2:

$ g++ main.cpp struct.cpp 
$ time a.out 

real 0m2.981s 
user 0m2.876s 
sys  0m0.079s 

$ g++ -O2 main.cpp struct.cpp 
$ time a.out 

real 0m1.629s 
user 0m1.523s 
sys  0m0.041s 

...如果你真的被删除struct.cpp和移动实现所有内嵌起飞优化眼罩:

$ time a.out 

real 0m0.550s 
user 0m0.543s 
sys  0m0.000s