右值引用是否与左值引用具有相同的开销?
Do rvalue references have the same overhead as lvalue references?
考虑这个例子:
#include <utility>
// runtime dominated by argument passing
template <class T>
void foo(T t) {}
int main() {
int i(0);
foo<int>(i); // fast -- int is scalar type
foo<int&>(i); // slow -- lvalue reference overhead
foo<int&&>(std::move(i)); // ???
}
foo<int&&>(i)
是否和 foo<int>(i)
一样快,还是像 foo<int&>(i)
一样涉及指针开销?
编辑:按照建议,运行 g++ -S
为 foo<int>(i)
和 foo<int&>(i)
提供了相同的 51 行汇编文件,但 foo<int&&>(std::move(i))
导致71行汇编代码(看起来差异来自std::move
)。
编辑:感谢那些推荐 g++ -S
具有不同优化级别的人——使用 -O3
(并使 foo noinline
)我能够得到看起来像 xaxxon's solution.
在您的具体情况下,它们很可能都是一样的。使用 gcc -O3 从 godbolt 生成的代码是 https://godbolt.org/g/XQJ3Z4 for:
#include <utility>
// runtime dominated by argument passing
template <class T>
int foo(T t) { return t;}
int main() {
int i{0};
volatile int j;
j = foo<int>(i); // fast -- int is scalar type
j = foo<int&>(i); // slow -- lvalue reference overhead
j = foo<int&&>(std::move(i)); // ???
}
是:
mov dword ptr [rsp - 4], 0 // foo<int>(i);
mov dword ptr [rsp - 4], 0 // foo<int&>(i);
mov dword ptr [rsp - 4], 0 // foo<int&&>(std::move(i));
xor eax, eax
ret
volatile int j
是为了让编译器无法优化所有代码,否则它会知道调用的结果被丢弃,整个程序将优化为空。
但是,如果您强制函数不被内联,那么情况会有所改变 int __attribute__ ((noinline)) foo(T t) { return t;}
:
int foo<int>(int): # @int foo<int>(int)
mov eax, edi
ret
int foo<int&>(int&): # @int foo<int&>(int&)
mov eax, dword ptr [rdi]
ret
int foo<int&&>(int&&): # @int foo<int&&>(int&&)
mov eax, dword ptr [rdi]
ret
以上:https://godbolt.org/g/pbZ1BT
诸如此类的问题,学会爱https://godbolt.org and https://quick-bench.com/(快板凳需要你学习如何正确使用google测试)
参数传递的效率取决于 ABI。
例如,在 linux 上,Itanium C++ ABI 指定引用作为指向被引用对象的指针传递:
3.1.2 Reference Parameters
Reference parameters are handled by passing a pointer to the actual parameter.
这与参考类别(rvalue/lvalue 参考)无关。
为了更广泛的观点,我在丹麦技术大学 calling convention 的一份文件中找到了这句话,该文件分析了大多数编译器:
References are treated as identical to pointers in all respects.
因此右值和左值引用涉及所有 ABI 上的指针开销。
考虑这个例子:
#include <utility>
// runtime dominated by argument passing
template <class T>
void foo(T t) {}
int main() {
int i(0);
foo<int>(i); // fast -- int is scalar type
foo<int&>(i); // slow -- lvalue reference overhead
foo<int&&>(std::move(i)); // ???
}
foo<int&&>(i)
是否和 foo<int>(i)
一样快,还是像 foo<int&>(i)
一样涉及指针开销?
编辑:按照建议,运行 g++ -S
为 foo<int>(i)
和 foo<int&>(i)
提供了相同的 51 行汇编文件,但 foo<int&&>(std::move(i))
导致71行汇编代码(看起来差异来自std::move
)。
编辑:感谢那些推荐 g++ -S
具有不同优化级别的人——使用 -O3
(并使 foo noinline
)我能够得到看起来像 xaxxon's solution.
在您的具体情况下,它们很可能都是一样的。使用 gcc -O3 从 godbolt 生成的代码是 https://godbolt.org/g/XQJ3Z4 for:
#include <utility>
// runtime dominated by argument passing
template <class T>
int foo(T t) { return t;}
int main() {
int i{0};
volatile int j;
j = foo<int>(i); // fast -- int is scalar type
j = foo<int&>(i); // slow -- lvalue reference overhead
j = foo<int&&>(std::move(i)); // ???
}
是:
mov dword ptr [rsp - 4], 0 // foo<int>(i);
mov dword ptr [rsp - 4], 0 // foo<int&>(i);
mov dword ptr [rsp - 4], 0 // foo<int&&>(std::move(i));
xor eax, eax
ret
volatile int j
是为了让编译器无法优化所有代码,否则它会知道调用的结果被丢弃,整个程序将优化为空。
但是,如果您强制函数不被内联,那么情况会有所改变 int __attribute__ ((noinline)) foo(T t) { return t;}
:
int foo<int>(int): # @int foo<int>(int)
mov eax, edi
ret
int foo<int&>(int&): # @int foo<int&>(int&)
mov eax, dword ptr [rdi]
ret
int foo<int&&>(int&&): # @int foo<int&&>(int&&)
mov eax, dword ptr [rdi]
ret
以上:https://godbolt.org/g/pbZ1BT
诸如此类的问题,学会爱https://godbolt.org and https://quick-bench.com/(快板凳需要你学习如何正确使用google测试)
参数传递的效率取决于 ABI。
例如,在 linux 上,Itanium C++ ABI 指定引用作为指向被引用对象的指针传递:
3.1.2 Reference Parameters
Reference parameters are handled by passing a pointer to the actual parameter.
这与参考类别(rvalue/lvalue 参考)无关。
为了更广泛的观点,我在丹麦技术大学 calling convention 的一份文件中找到了这句话,该文件分析了大多数编译器:
References are treated as identical to pointers in all respects.
因此右值和左值引用涉及所有 ABI 上的指针开销。