C ++小对象性能

时间:2013-07-06 15:45:50

标签: c++ optimization gcc visual-studio-2012

在我的程序代码中,存在各种相当小的对象,范围从一个字节或2到大约16。 Vector2(2 * T),Vector3(3 * T),Vector4(4 * T),ColourI32(4),LightValue16(2),Tile(2)等(括号中的字节大小)。

进行了一些分析(基于样本),这导致了一些比预期更慢的函数,例如

//4 bits per channel natural light and artificial RGB
class LightValue16
{
...
    explicit LightValue16(uint16_t value);
    LightValue16(const LightValueF &);
    LightValue16(int r, int g, int b, int natural);

    int natural()const;
    void natural(int v);
    int artificialRed()const;
    ...
    uint16_t data;
};
...
LightValue16 World::getLight(const Vector3I &pos)
{ ... }

此函数通过几个数组进行一些数学运算来查找值,其中一些默认值位于世界的填充部分之上。内容很好地内联,看着反汇编看起来尽可能好。有大约100个指令。然而有一点是突出的,在所有返回网站上它实现了类似的东西:

mov eax, dword pyt [ebp + 8]
mov cx, word ptr[ecx + edx * 2] ; or say mov ecx, Fh
mov word ptr [eax], cx
pop ebp
ret 10h

对于x64,我看到了几乎相同的东西。我没有检查我的GCC构建,但我怀疑它几乎完全相同。

我做了一些实验,并使用uint16_t返回类型找到了。它实际上导致了World :: getLight函数内联(看起来几乎相同的核心80指令左右,没有条件/循环不同的作弊)和我正在调查的外部函数的总CPU使用率从16.87 %到14.04%虽然我可以根据具体情况来做这件事(同时尝试强制内联的东西),有什么实际的方法可以避免这样的性能问题开始吗?甚至可能在整个代码中加快几个百分点?

我刚才能想到的最好的方法是在这种情况下使用原始类型(< 4或者8字节对象)并将所有当前成员的东西移动到非成员函数中,所以更像是在C中完成的,只是使用命名空间。

考虑到这一点,我想在“t foo(浮动x,浮动y,浮动z)”之类的“t foo(const Vector3F& p)”之类的东西经常会花费成本吗?如果是这样,通过广泛使用const&的程序,它是否会增加显着差异?

2 个答案:

答案 0 :(得分:2)

看看Itanium C++ ABI。虽然您的计算机肯定没有Itanium处理器,但gcc对x86和x86-64 ABI进行了非常类似于Itanium ABI的建模。链接部分说明了

  

但是,如果返回值类型具有非平凡的复制构造函数或析构函数,则[返回调用者提供的内存发生]

要找出非平凡的复制构造函数或析构函数的含义,请查看What are Aggregates and PODs and how/why are they special?,并查看类的规则是“可以轻易复制”的。在您的情况下,问题是您定义的复制构造函数。它根本不需要,编译器将合成一个复制构造函数,只需根据需要分配data成员。如果你想明确声明你想要一个拷贝构造函数,并且你正在使用C ++ 11,你也可以把它写成默认函数,这不会使它变得非常重要:

LigthValue16(const LightValue16 & other) = default;

答案 1 :(得分:0)

在对这个问题的评论中,已经有很多讨论,是否允许编译器将class LightValue16作为您所分析函数的简单uint16_t来处理。

如果你的类没有特殊的魔法(比如虚函数),整个类对于分析的函数是可见的,编译器可以生成100%同等的代码高效然后只使用`uint16_t类型。

问题是“可以”。尽管所有体面的编译器通常都会生成100%的快速代码,但偶尔也会出现一些优化情况,或者至少生成的代码会有所不同。 它可能只是一个启发式参数的变化(例如内联不会被应用,因为在某些优化步骤中只有一点点代码因为类而保留)或者某些优化过程在这个阶段真的需要一个简单的数字类型,这甚至不是编译器中的真正错误。 例如,如果您添加“模板< bool NotUsed>”在上面的课程中,这可能会改变编译器中的优化步骤,尽管语义上你的程序不会改变。

因此,如果您想100%确定,请直接使用int或double。但是在90%的时间内,它将以100%的速度快速增长,仅在10%的情况下,它只有90%的性能,应该是O.K.所有用例的99%(但不是100%)。