如果我有两个结构:
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 的 operator* 与 A 的 operator* 相比有任何不同的编译方式或运行得更慢或更快的原因(函数内部进行的实际操作应该无关紧要)?
我的意思是...将内联运算符声明为成员,而不是声明为成员,对实际函数的速度有任何一般影响吗?
我有许多不同的结构,它们目前都遵循内联成员运算符风格……但我想改为将其修改为有效的 C 代码;所以在我这样做之前,我想知道性能/编译是否会有任何变化。
最佳答案
按照您编写的方式,我希望 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
和 B
实现相同的功能,如果你使用引用编写它并将其设为 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;
}
您仍然希望返回对象,而不是引用,因为结果实际上是临时的(您不会返回修改后的现有对象)。
附录
However, with the const pass-by-reference for both arguments, in B, would it make it effectively faster than A, due to the dereferencing?
首先,当您拼出所有代码时,两者都涉及相同的解除引用。 (请记住,访问 this
的成员意味着指针取消引用。)
但即便如此,这也取决于您的编译器的智能程度。在这种情况下,假设它查看了您的结构并决定不能将其填充到寄存器中,因为它是两个 float ,因此它将使用指针来访问它们。因此,解除引用的指针情况(这是实现引用的方式)是您将获得的最好的情况。程序集看起来像这样(这是伪汇编代码):
// 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
以获得最终代码的反汇编,以查看您的案例中到底发生了什么。
关于c++ - 内联成员运算符与内联运算符 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10670560/