哪个更有效:返回值与通过引用传递?

the*_*edi 70 c++ performance function return-value pass-by-reference

我目前正在研究如何编写高效的C++代码,在函数调用方面,我想到了一个问题.比较这个伪代码功能:

not-void function-name () {
    do-something
    return value;
}
int main () {
    ...
    arg = function-name();
    ...
}
Run Code Online (Sandbox Code Playgroud)

使用其他相同的伪代码功能:

void function-name (not-void& arg) {
    do-something
    arg = value;
}
int main () {
    ...
    function-name(arg);
    ...
}
Run Code Online (Sandbox Code Playgroud)

哪个版本更有效,以及在什么方面(时间,内存等)?如果它取决于,那么第一个什么时候会更有效率,什么时候第二个会更高效?

编辑:对于上下文,此问题仅限于与硬件平台无关的差异,并且大多数情况下也是软件.是否有任何与机器无关的性能差异?

编辑:我不知道这是一个重复.另一个问题是将参考传递(上游代码)与传递值(下图)进行比较:

not-void function-name (not-void arg)
Run Code Online (Sandbox Code Playgroud)

这跟我的问题不一样.我的重点不在于将参数传递给函数的更好方法.我关注的是这是更好的方式来传递一个结果来自外部范围的变量.

aro*_*ire 24

首先,采取帐户返回一个对象总是比有它引用传递更具可读性(而且在性能上非常相似),因此可能会更有趣的为您的项目返回对象和增加可读性,而无需重要的性能差异.如果您想知道如何获得最低成本,那么您需要返回的是:

  1. 如果需要返回简单或基本对象,则两种情况下的性能都相似.

  2. 如果对象是如此庞大和复杂,返回它将需要一个副本,它可能比将它作为引用参数慢,但它会花费更少的内存,我认为.

无论如何,你必须考虑编译器做了很多优化,使两个表演非常相似.请参见复制Elision.

  • 实际上,在x86上 - 并且忽略编译器优化 - 两者都会创建相同的汇编代码,因为返回值大于1或2?寄存器通过内存区域传递,该内存区域由调用者分配并通过隐式指针参数传递给被调用者. (8认同)
  • 复制省略呢? (2认同)

Dav*_*aim 8

嗯,必须明白编译不是一件简单的事情.编译器编译代码时需要考虑很多因素.

人们不能简单地回答这个问题,因为C++标准不提供标准的ABI(抽象二进制接口),因此允许每个编译器编译任何它喜欢的代码,并且每次编译都可以得到不同的结果.

例如,在某些项目中,C++被编译为Microsoft CLR(C++/CX)的托管扩展.既然所有东西都已经有了对堆上对象的引用,我想没有区别.

对于未经管理的编译,答案并不简单.当我想到"XXX会比YYY跑得更快吗?"时会想到几个问题,例如:

  • 你是否反对建构?
  • 您的编译器是否支持返回值优化?
  • 您的对象是否支持仅复制语义,还是支持复制和移动?
  • 对象是以有条不紊的方式打包(例如std::array)还是指向堆上的东西?(例如std::vector)?

如果我举一个具体的例子,我的猜测是在MSVC++和GCC上,std::vector按值返回将是通过引用传递它,因为r值优化,并且会比返回向量快一点(几纳秒)通过移动.例如,对于Clang来说,这可能完全不同.

最终,剖析是唯一真正的答案.


Sim*_*ple 7

在大多数情况下,应该使用返回对象,因为称为复制省略的操作.

但是,根据您的函数的使用方式,最好通过引用传递对象.

std::getline例如,看一下std::string参考.此功能旨在用作循环条件并保持填充std::string直到达到EOF.使用相同的方法std::string允许std::string在每次循环迭代中重用存储空间,从而大大减少了需要执行的内存分配数量.


Val*_*ity 5

一些答案已经涉及到这一点,但我想根据编辑强调一下

对于上下文,此问题仅限于与硬件平台无关的差异,并且大多数情况下也仅限于软件.是否有任何与机器无关的性能差异?

如果这是问题的限制,答案是没有答案.c ++规范没有规定如何以性能方式实现对象的返回或通过引用的传递,只是它们在代码方面所做的语义.

因此,编译器可以自由地优化一个与另一个相同的代码,假设这不会给程序员带来明显的差异.

鉴于此,我认为最好使用最直观的情况.如果函数确实作为某个任务或查询的结果"返回"了一个对象,则将其返回,而如果该函数正在对外部代码所拥有的某个对象执行操作,则通过引用传递.

你无法概括性能.首先,做一些直观的事情,看看你的目标系统和编译器如何优化它.如果在分析后发现问题,请根据需要进行更改.


Nam*_*man 2

这个伪代码函数:

not-void function-name () {
    do-something
    return value;
}
Run Code Online (Sandbox Code Playgroud)

当返回值不需要对其进行任何进一步修改时,会更好地使用。传递的参数仅在function-name. 不需要更多的参考资料。


其他方面相同的伪代码函数:

void function-name (not-void& arg) {
    do-something
    arg = value;
}
Run Code Online (Sandbox Code Playgroud)

如果我们有另一种方法来调节同一变量的值,并且我们需要保留通过任一调用对变量所做的更改,那么这将很有用。

void another-function-name (not-void& arg) {
    do-something
    arg = value;
}
Run Code Online (Sandbox Code Playgroud)