2015-08-28 90 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