6

考虑这个例子:

#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给了我相同的 51 行汇编文件foo<int>(i)foo<int&>(i),但foo<int&&>(std::move(i))产生了 71 行汇编代码(看起来差异来自std::move)。

编辑:感谢那些推荐g++ -S不同优化级别的人——使用-O3(和制作 foo noinline)我能够获得看起来像xaxxon 解决方案的输出输出。

4

2 回答 2

6

在您的具体情况下,它们很可能都是相同的。godbolt 与 gcc -O3 生成的代码是https://godbolt.org/g/XQJ3Z4用于:

#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.orghttps://quick-bench.com/(quick bench 需要你学会如何正确使用google test

于 2018-08-14T03:15:13.027 回答
5

参数传递的效率取决于 ABI。

例如,在 linux 上,Itanium C++ ABI指定引用作为指向被引用对象的指针传递:

3.1.2 参考参数

引用参数通过传递指向实际参数的指针来处理。

这与参考类别(右值/左值参考)无关。

从更广泛的角度来看,我在丹麦技术大学的一份文档中找到了这句话,称为约定,它分析了大多数编译器:

引用在所有方面都被视为与指针相同。

所以右值和左值引用涉及所有 ABI 的指针开销。

于 2018-08-14T10:43:49.330 回答