哪个更快?按引用传递与按值传递 C++

Posted

技术标签:

【中文标题】哪个更快?按引用传递与按值传递 C++【英文标题】:Which is faster? Pass by reference vs pass by value C++ 【发布时间】:2014-12-20 13:27:41 【问题描述】:

我认为通过引用传递应该比通过值传递更快,因为计算机不是在复制数据,它只是指向数据的地址。

但是,请考虑以下 C++ 代码:

#include <iostream>
#include <cassert>
#include <cmath>

using namespace std;

// do not use pass by reference& with this function, it will become so slow
unsigned long long computePascal(const unsigned int row, const unsigned int position) 
    if (position == 1 || position == row)
    return 1L;
    unsigned long long left = computePascal(row-1, position-1);
    unsigned long long right = computePascal(row-1, position);
    unsigned long long sum = left + right;
    return sum;


int main() 
    int row, position;
    cout << "Input row and position: ";
    cin >> row >> position;
    assert(position > 0 && position <= row);
    unsigned long long pascalNumber = computePascal(row, position);
    cout << pascalNumber << endl;
    return 0;

现在这段代码只是一个普通程序,它通过输入三角形的所需行和位置递归地计算帕斯卡数。

我尝试放置第 50 行和第 7 位,它计算大约 1 秒(按值传递)。输出大约是 1300 万,这是正确的。所以我认为如果我通过引用传递它可能会更快,因为它不需要复制大量数据。但这是非常错误的,我不知道为什么它需要更长的时间,大约是传递价值所需的时间的 3 倍。 问题是……

为什么在我尝试将其更改为通过 const 引用传递后,该程序的计算速度如此之慢?

因为递归函数是一个例外,你应该通过值而不是 const 引用传递,这有关系吗?

【问题讨论】:

“不是在复制数据,它只是指向数据的地址”——如果数据小于地址,那么复制它可能比通过函数的地址。并且通过引用传递会增加一个间接级别,这是有代价的。 int* 类型至少与 int 本身一样大,因此您不会在复制时节省任何时间,并且必须在其之上执行几个地址和取消引用操作。 没有明确的规则哪一个更快。当整数通过值传递时,它们可以存储在寄存器中,速度非常快。由于这个原因,通常您应该按值传递基本数据类型。通常只有在复制类型的成本很大时才值得。 只是说:如果你的计算时间超过几微秒,你就做错了。我会说不恰当地使用递归。如果结果为 n,那么您正在拨打 n 次电话。 哦,我忘了认为地址大于数据本身。谢谢你的澄清。只需回答,我会将您标记为最佳答案并关闭线程。我从 learncpp.com 得到这个想法,您应该始终使用按 const 引用传递,而不是一直按值传递,因为不需要克隆数据。所以我认为现在这是错误的。但是这个问题有什么问题呢?因为我不知道所以我被否决了?还是这个问题对其他人来说太容易了? 【参考方案1】:

“哪个更快”的答案通常是“视情况而定”。

如果您不是传递四个字节的数据,而是传递一个指向数据的八字节指针,那么您就不能真正期望这会使事情变得更快。如果不是传递 100 个字节的数据,而是传递一个指向数据的 8 字节指针,那就不同了。

但现在函数没有数据,它只有一个引用。所以每当它需要读取数据时,它必须通过引用间接地做到这一点。那需要更长的时间。如果你传递了一个 100 字节的对象并且只读取了其中的 8 个字节,那么你仍然有可能获胜。但是,如果您实际上读取了所有数据,并且可能多次读取,那么即使对于大型对象,传递值也可能会更快。

真正的区别在于传递对象时,而按值传递意味着将调用或多或少复杂的构造函数。通过引用传递意味着没有构造函数。但是 int 无论如何都没有构造函数。

然后是优化。按值传递意味着编译器知道您的函数是唯一可以访问数据的函数。通过引用传递意味着数据可以在任何地方。如果您有两个 int& 参数,我可以将 some int 传递两次。所以增加行可能会增加 pos。或者它可能不会。这会扼杀优化。

然后是优化规则:“衡量它”。你测量了它,发现什么更快。有时事情变得更快或更慢,没有任何充分的理由。

【讨论】:

在内存分配方面,引用传递和指针传递是一样的吗? 没有。传递引用不会像按值传递所有内容(副本)那样增加堆栈。 @off99555 是的,如果您完全遵循通过引用传递的假设,即引用值可能会在调用函数内部更改,因此必须将 row 的解引用减去一个另一次,即使row 本身从未改变。如果我们通过您的函数将参数转换为引用,那么它与参数通过指针传递的函数相同,加上堆栈值的定义以及地址传递给递归调用的算术。例如,请参阅godbolt.org/z/o5NRoI。 值通常不是更慢,因为您几乎无法利用 CPU 缓存来处理大型对象吗?我认为性能通常涉及今天尽可能少地去记忆。

以上是关于哪个更快?按引用传递与按值传递 C++的主要内容,如果未能解决你的问题,请参考以下文章

java的按值传递与按引用传递

Common Lisp:按值传递与按引用传递[重复]

在 C# 中将 REF 和 OUT 关键字与按引用传递和按值传递一起使用

Java:按值传递与按引用传递

C#在方法调用中,参数按值传递与按引用传递的区别是啥?

在 C++ 中通过引用与按值将向量传递给函数