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

Ser*_*rge 3 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;
    } 
}
Run Code Online (Sandbox Code Playgroud)

和一个等效的结构

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;
} 
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mik*_*one 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;
}
Run Code Online (Sandbox Code Playgroud)

因此,A将一个指向其左侧参数的指针传递给函数,同时B在调用函数之前必须复制该参数.两者都必须复制他们的右侧参数.

您的代码会好得多,而且很可能会实现相同的AB,如果你写使用引用它,并使它const正确的:

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;
}
Run Code Online (Sandbox Code Playgroud)

您仍然希望返回对象而不是引用,因为结果实际上是临时的(您没有返回修改后的现有对象).


附录

但是,对于两个参数的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
Run Code Online (Sandbox Code Playgroud)

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

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

随意传递-S选项以gcc获取最终代码的反汇编,以查看在您的情况下真正发生的事情.