按值传递与按引用或指针传递的性能成本?

新手上路,请多包涵

Let’s consider an object foo (which may be an int , a double , a custom struct , a class ,无论如何)。我的理解是通过 foo 通过引用一个函数(或只是传递一个指向 foo 的指针)会导致更高的性能,因为我们避免制作本地副本(如果 foo 很大)。

但是,从 这里 的答案看来,无论指向什么,实际上都可以预期 64 位系统上的指针的大小为 8 个字节。在我的系统上,一个 float 是 4 个字节。这是否意味着如果 foo 的类型是 foo float ,那么只通过值而不是给出指向它的指针(假设没有其他约束会使在函数内使用一个比另一个更有效)?

原文由 space_voyager 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 990
2 个回答

这取决于您所说的“成本”以及主机系统(硬件、操作系统)相对于操作的属性。

如果您的成本衡量标准是内存使用量,那么成本的计算是显而易见的 - 将正在复制的任何内容的大小相加。

如果您的衡量标准是执行速度(或“效率”),那么游戏就不同了。借助专用电路(机器寄存器及其使用方式),硬件(以及操作系统和编译器)倾向于针对复制特定大小的事物的操作性能进行优化。

例如,对于一台机器来说,通常有一个架构(机器寄存器、内存架构等)会导致“最佳位置”——复制某种大小的变量是最“有效的”,但复制更大或更小的变量是少这样。较大的变量将花费更多的复制成本,因为可能需要对较小的块进行多次复制。较小的也可能成本更高,因为编译器需要将较小的值复制到较大的变量(或寄存器)中,对其进行操作,然后将值复制回来。

浮点示例包括一些 cray 超级计算机,它们本机支持双精度浮点(在 C++ 中又名 double ),所有单精度操作(在 C++ 中又名 float )都在软件。一些较旧的 32 位 x86 CPU 在内部也使用 32 位整数,并且由于与 32 位之间的转换,对 16 位整数的操作需要更多的时钟周期(这不适用于更现代的 32 位或 64-位 x86 处理器,因为它们允许将 16 位整数复制到 32 位寄存器或从 32 位寄存器复制,并对其进行操作,这样的惩罚较少)。

按值复制一个非常大的结构将比创建和复制其地址效率低,这有点不费吹灰之力。但是,由于上述因素,“最好按值复制该大小的东西”和“最好传递其地址”之间的交叉点不太清楚。

指针和引用倾向于以类似的方式实现(例如,按引用传递可以以与传递指针相同的方式实现),但这不能保证。

唯一确定的方法是测量它。并意识到测量值会因系统而异。

原文由 Peter 发布,翻译遵循 CC BY-SA 3.0 许可协议

没有人提到一件事。

有一种称为 IPA SRA 的 GCC 优化,它自动将“按引用传递”替换为“按值传递”: https ://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html (-fipa-sra)

这很可能适用于标量类型(例如 int、double 等),它们没有非默认复制语义并且可以放入 cpu 寄存器。

这使得

void(const int &f)

可能一样快(并且空间优化)

 void(int f)

因此,启用此优化后,使用小类型的引用应该与按值传递它们一样快。

另一方面,按值传递(例如)std::string 无法优化到按引用速度,因为涉及到自定义复制语义。

据我了解,对所有内容使用按引用传递永远不会比手动选择按值传递的内容和按引用传递的内容要慢。

这对于模板特别有用:

 template<class T>
void f(const T&)
{
    // Something
}

总是最优的

原文由 peku33 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题