我做了一些测试,我有两个场景,每个场景都有两个相同的函数——一个通过引用传递参数,另一个通过值传递。带有字符串的场景显示出巨大的性能提升(因为制作了字符串的副本,调用了构造函数),而带有 long 的测试在通过引用传递值时没有显示任何性能提升。事实上,有时性能更差。
这是原始类型的预期吗?通过引用传递它们没有意义吗?
我期望在不通过引用使用时会生成原始类型的副本,因此期望性能会有所提升。
我做了一些测试,我有两个场景,每个场景都有两个相同的函数——一个通过引用传递参数,另一个通过值传递。带有字符串的场景显示出巨大的性能提升(因为制作了字符串的副本,调用了构造函数),而带有 long 的测试在通过引用传递值时没有显示任何性能提升。事实上,有时性能更差。
这是原始类型的预期吗?通过引用传递它们没有意义吗?
我期望在不通过引用使用时会生成原始类型的副本,因此期望性能会有所提升。
您可以通过按值传递原始类型来获得最佳性能。这是因为:
double
中long long
都比参考大最后一点经常被忽视,但可以产生相当大的影响。
是的,这是预期的行为。当您通过引用传递参数时,您实际上是在传递变量的地址(如使用指针)。通常地址是一个 4 或 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
解决方案,编译器可以使用寄存器,这要快得多。
在 c++ 中,引用只是使用指针的便捷方式。当您使用指针时,您会添加额外的间接。复制原始类型与复制指针一样便宜。这就是为什么通过引用传递的原始类型要慢一些
当您通过引用传递一个值时,该函数必须取消引用它以获得该值,并且每次修改该值时也必须发生取消引用,因为您将它写入内存位置。我猜编译器能够理解什么时候不会将某些内容存储回其参考位置,以便仅在寄存器上修改值并仅在需要时存储回来,但我不确定这有多强大。
因此,在按值传递参数时有一个间接步骤不存在,这可能会导致更差的性能,但它真的很模糊,因为编译器优化已经到位。考虑一下您正在传递一个指针的事实,每次您需要该值时,您必须从堆栈中获取指针,然后获取指向的值(因此有两次访问),而使用普通参数时您只有一个。
在任何情况下,参考都用于与性能肯定不同的目的。
因为您使用c
了标签,所以我猜您是在谈论指针(不是来自 C++ 的显式引用)。
使用指针,您有两种内存访问:指针和指向的值。所以表演并没有特别的收获。此外,编译器可以对值进行更多优化:例如,没有别名问题。
这是原始类型的预期吗?
我会说绝对。它们没有构造函数,因此不需要调用。
通过引用传递它们没有意义吗?
有:当你想要输出参数时,在 C++ 中,通过引用传递被认为是比传递指针更好的实践。
我期望在不通过引用使用时会生成原始类型的副本,因此期望性能会有所提升。
好吧,由于通过引用传递通常是使用指针实现的,因此编译器必须发出将某些内容(值或指向该值的指针)压入堆栈的代码 - 执行哪一个实际上并不重要。