c++ - "Bad"GCC优化性能

标签 c++ gcc optimization g++ mingw

我试图理解为什么在 GCC 中使用 -O2 -march=native 会产生比不使用它们时更慢的代码。 请注意,我在 Windows 7 下使用 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;
}

ma​​in.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 main.o

mingw32-g++.exe -Wall -fexceptions -std=c++11 -c struct.cpp -o struct.o

mingw32-g++.exe -o 程序.exe main.o struct.o -s

2:使用-O2优化

mingw32-g++.exe -Wall -fexceptions -O2 -march=native -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 程序.exe main.o struct.o -s

1:执行时间:

1196 毫秒(使用 Visual Studio 2013 为 1269 毫秒)

2:执行时间:

1569 毫秒(使用 Visual Studio 2013 为 403 毫秒)!!!!!!!!!!!!!

使用 -O3 代替 -O2 不会改善结果。 我曾经并且现在仍然非常确信 GCC 和 Visual Studio 是等效的,所以我不理解这种巨大的差异。 另外,我不明白为什么优化版本比 GCC 的非优化版本慢。

我错过了什么吗? (请注意,我在 Ubuntu 上使用正版 GCC 4.8.2 也遇到了同样的问题)

感谢您的帮助

最佳答案

考虑到我没有看到汇编代码,我将推测以下内容:

可以通过删除 if 子句并导致以下结果来优化分配循环(由编译器):

 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 x 4,而正方形分配为 4 x 4。

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

接下来,您将以正常顺序迭代 vector 10000 次(正常情况下,我的意思是一个又一个索引)。

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

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

通过删除 i%2 进行测试,看看会得到什么结果。

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

关于c++ - "Bad"GCC优化性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32271272/

相关文章:

c++ - C++ 类中的指针成员变量初始化

c++ - 在 C++ 中以 'D' 作为指数前缀读取双科学记数法

C++ qt基础问题

c++ - WNetGetUniversalName 的问题

c++ - 从 Foo** 到 void** 的无效转换 - 为什么允许隐式类型转换为 void* 但不允许为 void**?

c++ - 创建多少个线程?

c - 如何根据架构一般指定 AX、EAX 或 RAX?

c - 从位图中移除 Alpha channel (RGBA 到 RGB)

java - 有没有支持快速插入和中值计算的数据结构?

python - 有效地重新堆叠 numpy ndarray