25

可能重复:
基本数据类型是按值传递还是通过引用传递更好?
不通过引用传递简单类型的原因?

我做了一些测试,我有两个场景,每个场景都有两个相同的函数——一个通过引用传递参数,另一个通过值传递。带有字符串的场景显示出巨大的性能提升(因为制作了字符串的副本,调用了构造函数),而带有 long 的测试在通过引用传递值时没有显示任何性能提升。事实上,有时性能更差。

这是原始类型的预期吗?通过引用传递它们没有意义吗?

我期望在不通过引用使用时会生成原始类型的副本,因此期望性能会有所提升。

4

7 回答 7

40

您可以通过按值传递原始类型来获得最佳性能。这是因为:

  • 原语是 blitable 的,因此副本的成本取决于大小
  • 原语很小,只有在大多数环境doublelong long都比参考大
  • pass-by-value 避免了别名,允许优化器真正做它的事情

最后一点经常被忽视,但可以产生相当大的影响。

于 2012-12-23T17:46:10.563 回答
11

是的,这是预期的行为。当您通过引用传递参数时,您实际上是在传递变量的地址(如使用指针)。通常地址是一个 4 或 8 字节的整数,所以除非你的原始类型大于那个,否则你不会获得任何性能改进(即使它更大,你也可能不会)

于 2012-12-23T17:45:20.213 回答
8

现代编译器非常聪明,所以如果函数不是“隐藏的”(即编译器在生成代码时看不到的一部分),它可能根本没有任何区别。但是,如果编译器按照您的说明进行操作,则将简单类型作为引用传递可能会产生很大的不同。特别是如果值在代码中多次更新。

我在我工作的地方看到了一些代码,它做了这样的事情:

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     len = 0;
     while (list)
     {
         len++;
         list = list->next;
     }
 }

通过改变代码来做:

void SomeClass::FindLength(int &len)
{
     listEntry* list = theList;   // theList is a member variable. 
     int tempLen = 0;
     while (list)
     {
         tempLen++;
         list = list->next;
     }
     len = tempLen;
 }

整个代码的运行速度提高了 30%,并且从很多地方调用(我认为中间有一些 if 条件,所以我们不能只跟踪长度)。由于它是 API 函数的一部分,因此无法更改函数签名。

使用引用较慢的原因是编译器每次更新时都会写入引用值,这是从内存加载到寄存器、递增寄存器和将寄存器存储到内存。使用该tempLen解决方案,编译器可以使用寄存器,这要快得多。

于 2012-12-23T18:16:34.533 回答
4

在 c++ 中,引用只是使用指针的便捷方式。当您使用指针时,您会添加额外的间接。复制原始类型与复制指针一样便宜。这就是为什么通过引用传递的原始类型要慢一些

于 2012-12-23T17:44:45.433 回答
2

当您通过引用传递一个值时,该函数必须取消引用它以获得该值,并且每次修改该值时也必须发生取消引用,因为您将它写入内存位置。我猜编译器能够理解什么时候不会将某些内容存储回其参考位置,以便仅在寄存器上修改值并仅在需要时存储回来,但我不确定这有多强大。

因此,在按值传递参数时有一个间接步骤不存在,这可能会导致更差的性能,但它真的很模糊,因为编译器优化已经到位。考虑一下您正在传递一个指针的事实,每次您需要该值时,您必须从堆栈中获取指针,然后获取指向的值(因此有两次访问),而使用普通参数时您只有一个。

在任何情况下,参考都用于与性能肯定不同的目的。

于 2012-12-23T17:46:02.060 回答
1

因为您使用c了标签,所以我猜您是在谈论指针(不是来自 C++ 的显式引用)。

使用指针,您有两种内存访问:指针和指向的值。所以表演并没有特别的收获。此外,编译器可以对值进行更多优化:例如,没有别名问题。

于 2012-12-23T17:45:24.153 回答
1

这是原始类型的预期吗?

我会说绝对。它们没有构造函数,因此不需要调用。

通过引用传递它们没有意义吗?

有:当你想要输出参数时,在 C++ 中,通过引用传递被认为是比传递指针更好的实践。

我期望在不通过引用使用时会生成原始类型的副本,因此期望性能会有所提升。

好吧,由于通过引用传递通常是使用指针实现的,因此编译器必须发出将某些内容(值或指向该值的指针)压入堆栈的代码 - 执行哪一个实际上并不重要。

于 2012-12-23T17:46:04.230 回答