问题 如何在C ++中优化简单的数值类型包装类?


我试图用C ++实现一个定点类,但我遇到性能问题。我已经将问题简化为浮点类型的简单包装,但它仍然很慢。我的问题是 - 为什么编译器无法完全优化它?

'float'版本比'Float'快50%。为什么?!

(我使用Visual C ++ 2008,测试了所有可能的编译器选项,当然是Release配置)。

请参阅以下代码:

#include <cstdio>
#include <cstdlib>
#include "Clock.h"      // just for measuring time

#define real Float      // Option 1
//#define real float        // Option 2

struct Float
{
private:
    float value;

public:
    Float(float value) : value(value) {}
    operator float() { return value; }

    Float& operator=(const Float& rhs)
    {
        value = rhs.value;
        return *this;
    }

    Float operator+ (const Float& rhs) const
    {
        return Float( value + rhs.value );
    }

    Float operator- (const Float& rhs) const
    {
        return Float( value - rhs.value );
    }

    Float operator* (const Float& rhs) const
    {
        return Float( value * rhs.value );
    }

    bool operator< (const Float& rhs) const
    {
        return value < rhs.value;
    }
};

struct Point
{
    Point() : x(0), y(0) {}
    Point(real x, real y) : x(x), y(y) {}

    real x;
    real y;
};

int main()
{
    // Generate data
    const int N = 30000;
    Point points[N];
    for (int i = 0; i < N; ++i)
    {
        points[i].x = (real)(640.0f * rand() / RAND_MAX);
        points[i].y = (real)(640.0f * rand() / RAND_MAX);
    }

    real limit( 20 * 20 );

    // Check how many pairs of points are closer than 20
    Clock clk;

    int count = 0;
    for (int i = 0; i < N; ++i)
    {
        for (int j = i + 1; j < N; ++j)
        {
            real dx = points[i].x - points[j].x;
            real dy = points[i].y - points[j].y;
            real d2 = dx * dx + dy * dy;
            if ( d2 < limit )
            {
                count++;
            }
        }
    }

    double time = clk.time();

    printf("%d\n", count);
    printf("TIME: %lf\n", time);

    return 0;
}

1232
2017-07-19 08:52


起源

你打开了最大优化标志吗?当你把它们打开时,我看到了魔法发生。 - iammilind
生成装配并检查差异所在的位置...... - Matthieu M.
您可以尝试将所有方法明确标记为“内联” - Ludger Sprenker
顺便说一下,如果你的类实现了定点运算而不是浮点运算,你能不能把它命名为“Fixed”而不是“Float”?你的同事会感谢你。 - R. Martinho Fernandes
我玩了许多不同的优化选项而没有效果。我看过大会 - 没有'电话',所以内联工作。还有更多说明。看到: future-processing.com/~mczardybon/FloatVSfloat.png。 - Michal Czardybon


答案:


IMO,它与此有关 优化标志。我在g ++ linux-64机器上检查了你的程序。没有任何优化,它会给出与您所说的相同的结果 50% 减。

保持最大优化开启(即 -O4)。两个版本都相同。打开优化并检查。


4
2017-07-19 09:12



我已经安装了GCC,实际上它运行良好! GCC的时间是1.13秒,而VC ++则是1.70秒(浮点运算)或2.58秒(浮点数)。我还发现直接将'dx * dx + dy * dy'移动到这个条件可以将VC ++的性能提高21%! VC ++怎么可能优化得那么糟糕?!我打开了所有可能的优化选项,并测试了许多不同的组合。 - Michal Czardybon
哇...当我从'Win32'切换到'x64'平台时,执行时间从2.58下降到0.77秒! “浮动”和“浮动”也是一样的。 - Michal Czardybon


尝试  通过引用传递。你的类足够小,以至于通过引用传递它的开销(是的,如果编译器没有优化它就有开销),可能高于复制类。所以这...

Float operator+ (const Float& rhs) const
{
   return Float( value + rhs.value );
}

变成这样的......

Float operator+ (Float rhs) const
{
   rhs.value+=value;
   return rhs;
}

这避免了临时对象,并且可以避免指针解除引用的某些间接。


4
2017-07-19 09:25



我试了一下 - 不行。它甚至可以将时间进一步增加59%。 - Michal Czardybon


经过进一步调查后,我完全相信这是编译器优化流程的一个问题。在这个实例中生成的代码是显着的  与使用非封装的相比 浮动。我的建议是向微软报告这个潜在的问题,看看他们对此有什么看法。我还建议你继续实现这个类的计划定点版本,因为为整数生成的代码看起来是最佳的。


2
2017-07-19 09:15