内联成员运算符与内联运算符C ++

时间:2012-05-20 03:21:24

标签: c++ performance struct inline operator-keyword

如果我有两个结构:

struct A
{
    float x, y;
    inline A operator*(A b) 
    {
        A out;
        out.x = x * b.x;
        out.y = y * b.y;
        return out;
    } 
}

和一个等效的结构

struct B
{
    float x, y;
}

inline B operator*(B a, B b) 
{
    B out;
    out.x = a.x * b.x;
    out.y = a.y * b.y;
    return out;
} 

你知道B的运算符*有任何不同的编译方式,或运行速度比A的运算符更慢或更快*(函数内部的实际操作应该无关紧要)吗?

我的意思是......将内联运算符声明为成员,而不是作为成员,对实际函数的速度有任何一般影响吗?

我有许多不同的结构,目前遵循内联成员运算符样式......但我想将其修改为有效的C代码,而不是;所以在我这样做之前,我想知道性能/编译是否会有任何变化。

3 个答案:

答案 0 :(得分:10)

你写它的方式,我希望B::operator*运行稍慢。这是因为A::operator*的“幕后”实现就像:

inline A A::operator*(A* this, A b) 
{ 
    A out;
    out.x = this->x * b.x;
    out.y = this->y * b.y;
    return out;
}

因此A将指向其左侧参数的指针传递给函数,而B必须在调用函数之前复制该参数。两者都必须复制他们的右手边参数。

如果您使用引用编写代码并使其A正确,那么您的代码会更好,并且可能会为Bconst实现相同的代码:

struct A
{
    float x, y;
    inline A operator*(const A& b) const 
    {
        A out;
        out.x = x * b.x;
        out.y = y * b.y;
        return out;
    } 
}

struct B
{
    float x, y;
}

inline B operator*(const B& a, const B& b) 
{
    B out;
    out.x = a.x * b.x;
    out.y = a.y * b.y;
    return out;
}

你仍然希望返回对象,而不是引用,因为结果是有效的临时对象(你没有返回修改后的现有对象)。


<强>附录

  

但是,对于两个参数的const pass-by-reference,在B中,由于解除引用,它是否会使它比A更快?

首先,当您拼出所有代码时,两者都涉及相同的解除引用。 (请记住,访问this的成员意味着指针取消引用。)

但即便如此,这取决于你的编译器有多聪明。在这种情况下,让我们说它会查看你的结构并决定它不能将它填入寄存器,因为它是两个浮点数,所以它将使用指针来访问它们。所以解除引用的指针大小写(这是什么引用被实现为)是你得到的最好的。程序集看起来像这样(这是伪程序集代码):

// Setup for the function. Usually already done by the inlining.
r1 <- this
r2 <- &result
r3 <- &b

// Actual function.
r4 <- r1[0]
r4 <- r4 * r3[0]
r2[0] <- r4
r4 <- r1[4]
r4 <- r4 * r3[4]
r2[4] <- r4

这假设是类似RISC的架构(比如ARM)。 x86可能使用较少的步骤,但无论如何它都被指令解码器扩展到大约这个细节水平。关键在于它是寄存器中指针的所有固定偏移解引用,其速度与它将获得的速度差不多。优化器可以尝试更智能并跨多个寄存器实现对象,但是这种优化器更难编写。 (虽然我怀疑如果result仅仅是一个未保留的临时对象,LLVM类型的编译器/优化器可以轻松地进行优化。)

因此,由于您使用this,因此您有一个隐式指针取消引用。但是如果对象在堆栈上怎么办?没有帮助;堆栈变量变为堆栈指针(或帧指针,如果使用)的固定偏移量解引用。所以你要在最后的某个地方取消引用一个指针,除非你的编译器足够明亮,可以将你的对象带到多个寄存器中。

随意将-S选项传递给gcc以获取最终代码的反汇编,以查看您的案例中发生了什么。

答案 1 :(得分:3)

你真的应该将inline留给编译器。

也就是说,默认情况下,类定义中定义的函数(与A的情况一样)是inline。 <{1}}的{​​{1}}说明符没用。

更有趣的情况是在类定义之外有成员函数定义。在这里,如果你想向编译器提供一个提示(它可以随意忽略),这是经常使用的,并且指令应该在调用者内串行编译。

阅读C ++ FAQ 9

答案 2 :(得分:2)

以下是我将如何编写结构:

struct A
{
    float x, y;
    A(float ax, float ay) : x(ax), y(ay) { }
    A operator*(const A& b) const { return b(x * b.x, y * b.y); } 
}

要回答这个问题,是的,在某些情况下,将运算符编写为成员函数可能会稍微快一些,但不足以在代码中产生明显的差异。

一些注意事项:

  1. 不用担心使用inline关键字。优化编译器 做出自己的决定是什么,不应该内联。

  2. 使用初始化构造函数。这样做是因为他们改进了代码 可读性。知道他们可以带来小的睡眠 性能优势。

  3. 尽可能经常地通过const引用传递结构。

  4. 专注于编写风格不佳的代码。大多数代码是 足够快,如果不是,可能是因为某些事情 在算法或处理IO方面进行了抨击。