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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33994995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:19:56  来源:igfitidea点击:

Which is more efficient: Return a value vs. Pass by reference?

c++performancefunctionreturn-valuepass-by-reference

提问by thegreatjedi

I am currently studying how to write efficient C++ code, and on the matter of function calls, a question comes to mind. Comparing this pseudocode function:

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

not-void function-name () {
    do-something
    return value;
}
int main () {
    ...
    arg = function-name();
    ...
}

with this otherwise-identical pseudocode function:

使用这个其他相同的伪代码函数:

void function-name (not-void& arg) {
    do-something
    arg = value;
}
int main () {
    ...
    function-name(arg);
    ...
}

Which version is more efficient, and in what respect (time, memory etc.)? If it depends, then when would the first be more efficient and when would the more efficient be the second?

哪个版本更高效,在哪些方面(时间、内存等)?如果视情况而定,那么第一个什么时候更有效率,第二个什么时候更有效率?

Edit: For context, this question is limited to hardware platform-independent differences, and for the most part software too. Are there any machine-independent performance difference?

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

Edit: I don't see how this is a duplicate. The other question is comparing passing by reference (prev. code) to passing by value (below):

编辑:我不知道这是如何重复的。另一个问题是比较按引用传递(上一个代码)和按值传递(下面):

not-void function-name (not-void arg)

Which is not the same thing as my question. My focus is not on which is the better way to pass in an argument to a function. My focus is on which is the better way to pass outa result to a variable from the outside scope.

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

回答by arodriguezdonaire

First of all, take in account that returning an object will always be more readable (and very similar in performance) than having it passed by reference, so could be more interesting for your project to return the object and increase readability without having important performance differences. If you want to know how to have the lowest cost, the thing is what do you need to return:

首先,考虑到返回对象总是比通过引用传递更具可读性(并且性能非常相似),因此您的项目返回对象并提高可读性而不会产生重要的性能差异可能更有趣. 如果您想知道如何将成本降至最低,那么您需要返回什么:

  1. If you need to return a simple or basic object, the performance would be similar in both cases.

  2. If the object is so large and complex, returning it would need a copy, and it could be slower than having it as a referenced parameter, but it would spend less memory I think.

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

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

You have to think anyway that compilers do a lot of optimizations which make both performances very similar. See Copy Elision.

无论如何,您必须考虑编译器做了很多优化,这使得两种性能非常相似。请参阅复制省略

回答by David Haim

Well, one must understand that compilation is not an easy buisness. there are many consideration taken when the compiler compiles your code.

嗯,必须明白编译不是一件容易的事。编译器编译您的代码时会考虑许多因素。

One can't simply answer this question because the C++ standard doesn't provide standard ABI (abstract binary interface), so each compiler is allowed to compile the code whatever it likes and you can get different results in each compilation.

不能简单地回答这个问题,因为 C++ 标准没有提供标准的 ABI(抽象二进制接口),所以每个编译器都可以编译它喜欢的代码,你可以在每次编译中得到不同的结果。

For example, on some projects C++ is compiled to managed extension of Microsoft CLR (C++/CX). since everything there is already a reference to an object on the heap, I guess there is not difference.

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

The answer is not simpler for un-managed compilations. several quaestion come to mind when I think about "Will XXX run faster then YYY?", for example:

对于非托管编译,答案并不简单。当我想到“XXX 会比 YYY 运行得更快吗?”时,我会想到几个问题,例如:

  • Is you object deafult-constructible?
  • Does your compiler support return-value-optimization?
  • Does your object support Copy-only semantics or both copy and move?
  • Is the object packed in contigious manner (e.g. std::array) or it has pointer to something on the heap? (e.g. std::vector)?
  • 你的对象是默认构造的吗?
  • 您的编译器是否支持返回值优化?
  • 您的对象是否支持仅复制语义或同时支持复制和移动?
  • 对象是以连续方式打包的(例如std::array)还是它有指向堆上某物的指针?(例如std::vector)?

If I give concrete example, my guess is that on MSVC++ and GCC, returning std::vectorby value will be the as passing it by reference, because of r-value-optimization, and will be a bit(by few nanoseconds) faster then returning the vector by move. this may be completly different on Clang, for example.

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

eventually, profiling is the only true answer here.

最终,分析是这里唯一正确的答案。

回答by Simple

Returning the object should be used in most cases because of an optimsation called copy elision.

由于称为复制省略的优化,在大多数情况下应该使用返回对象。

However, depending on how your function is intended to be used, it may be better to pass the object by reference.

但是,根据您的函数打算如何使用,通过引用传递对象可能更好。

Look at std::getlinefor instance, which takes a std::stringby reference. This function is intended to be used as a loop condition and keeps filling a std::stringuntil EOF is reached. Using the same std::stringallows the storage space of the std::stringto be reused in every loop iteration, drastically reducing the number of memory allocations that need to be performed.

看看std::getline例如,它需要一个std::stringby 引用。此函数旨在用作循环条件并不断填充 astd::string直到达到 EOF。使用它std::string允许std::string在每次循环迭代中重用的存储空间,从而大大减少需要执行的内存分配数量。

回答by Vality

Some of the answers have touched on this, but I would like to emphasize in light of the edit

一些答案已经触及了这一点,但我想根据编辑强调

For context, this question is limited to hardware platform-independent differences, and for the most part software too. Are there any machine-independent performance difference?

就上下文而言,这个问题仅限于与硬件平台无关的差异,并且在大多数情况下也是软件。是否有任何独立于机器的性能差异?

If this is the limits of the question, the answer is that there is no answer. The c++ spec does not stipulate how either the return of an object or a passing by reference is implemented performance wise, only the semantics of what they both do in terms of code.

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

A compiler is therefore free to optimize one to identical code as the other assuming this does not create a perceptible difference to the programmer.

因此,编译器可以自由地将一个代码优化为与另一个相同的代码,前提是这不会对程序员造成明显的差异。

In light of this, I think it is best to use whichever is the most intuitive for the situation. If the function is indeed "returning" an object as the result of some task or query, return it, while if the function is performing an operation on some object owned by the outside code, pass by reference.

有鉴于此,我认为最好使用最适合这种情况的方法。如果函数确实“返回”一个对象作为某些任务或查询的结果,则返回它,而如果函数正在对外部代码拥有的某个对象执行操作,则通过引用传递。

You cannot generalize performance on this. As a start, do whatever is intuitive and see how well your target system and compiler optimizes it. If after profiling you will discover a problem, change it if you need to.

你不能概括这方面的表现。首先,做任何直观的事情,看看你的目标系统和编译器优化它的程度。如果在分析后发现问题,请根据需要进行更改。

回答by plugwash

We can't be 100% general because different platforms have different ABIs but I think we can make some fairly general statements that will apply on most implementations with the caveat that these things mostly apply to functions that are not inlined.

我们不能 100% 通用,因为不同的平台有不同的 ABI,但我认为我们可以做出一些适用于大多数实现的相当通用的声明,但需要注意的是,这些东西主要适用于未内联的函数。

Firstly lets consider primitive types. At a low level a parameter pass by reference is implemented using a pointer whereas primitive return values are typically passed literally in registers. So return values are likely to perform better. On some architectures the same applies to small structures. Copying a value small enough to fit in a register or two is very cheap.

首先让我们考虑原始类型。在低级别,通过引用传递参数是使用指针实现的,而原始返回值通常在寄存器中按字面传递。所以返回值可能会表现得更好。在某些架构上,这同样适用于小型结构。复制一个足够小的值以适合一两个寄存器是非常便宜的。

Now lets consider larger but still simple (no default constructors, copy constructors etc) return values. Typically larger return values are handled by passing the function a pointer to the location where the return value should be put. Copy elision allows the variable returned from the function, the temporary used for return and the variable in the caller that the result is placed into to be merged into one. So the basics of passing would be much the same for pass by reference and return value.

现在让我们考虑更大但仍然简单(没有默认构造函数、复制构造函数等)的返回值。通常,较大的返回值是通过向函数传递一个指向返回值应该放置的位置的指针来处理的。复制省略允许函数返回的变量、用于返回的临时变量和调用者中放置结果的变量合并为一个。因此,传递的基础知识对于通过引用传递和返回值是相同的。

Overall for primitive types I would expect return values to be marginally better and for larger but still simple types I would expect them to be the same or better unless your compiler is very bad at copy elision.

总的来说,对于原始类型,我希望返回值稍微好一点,对于较大但仍然简单的类型,我希望它们相同或更好,除非您的编译器在复制省略方面非常糟糕。

For types that use default constructors, copy constructors etc things get more complex. If the function is called multiple times then return values will force the object to be re-constructed each time whereas reference parameters may allow the data structure to be reused without being reconstructed. On the other hand reference parameters will force a (possibly unnecessary) construction before the function is called.

对于使用默认构造函数、复制构造函数等的类型,事情变得更加复杂。如果多次调用该函数,则返回值将强制每次都重新构造对象,而引用参数可能允许重用数据结构而无需重新构造。另一方面,引用参数将在调用函数之前强制进行(可能是不必要的)构造。

回答by Naman

This pseudocode function:

这个伪代码函数:

not-void function-name () {
    do-something
    return value;
}

would be better used when the returned value does not require any further modifications onto it. The parameter passed is only modified in the function-name. There are no more references required to it.

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



otherwise-identical pseudocode function:

其他相同的伪代码函数:

void function-name (not-void& arg) {
    do-something
    arg = value;
}

would be useful if we have another method moderating the value of the same variable like and we need to keep the changes made to the variable by either of the call.

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

void another-function-name (not-void& arg) {
    do-something
    arg = value;
}

回答by elnigno

Performance-wise, copies are generally more expensive, although the difference might be negligible for small objects. Also, your compiler might optimize a return copy into a move, making equivalent to passing a reference.

在性能方面,副本通常更昂贵,尽管对于小对象来说差异可能可以忽略不计。此外,您的编译器可能会将返回副本优化为移动,相当于传递引用。

I'd recommend not passing non-constreferences unless you have a good reason to. Use the return value (e.g. functions of the tryGet()sort).

const除非您有充分的理由,否则我建议不要传递非引用。使用返回值(例如tryGet()排序函数)。

If you want you can measure yourself the difference, as others have said already. Run the test code a few million times for both versions and see the difference.

如果你愿意,你可以衡量自己的差异,正如其他人已经说过的那样。对两个版本运行测试代码几百万次,看看有什么不同。