返回std :: pair与非const引用的传递

Set*_*son 3 c++ optimization assembly

为什么返回std::pairboost::tuple要少得多效率比参考返回?在我测试的实际代码中,通过非const引用而不是std::pair在内核中设置数据可以将代码加速20%.

作为一个实验,我查看了三个最简单的情况,包括将两个(预定义的)整数添加到两个整数:

  1. 使用内部内联函数通过引用修改整数

  2. 使用两个内部内联函数按值返回整数

  3. 使用内部内联函数返回复制到结果的std :: pair.

使用g++ -c $x -Wall -Wextra -O2 -S相同的汇编代码编译结果,以便按引用传递并按值返回整数:

__Z7getPairiRiS_:
LFB19:
    pushq   %rbp
LCFI0:
    leal    1023(%rdi), %eax
    addl    $31, %edi
    movl    %eax, (%rsi)
    movq    %rsp, %rbp
LCFI1:
    movl    %edi, (%rdx)
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

(通过参考代码:

#include <utility>

inline void myGetPair(const int inp, int& a, int& b) {
    a = 1023 + inp;
    b = 31 + inp;
}

void getPair(const int inp, int& a, int& b) {
    myGetPair(inp, a, b);
}
Run Code Online (Sandbox Code Playgroud)

使用单独的右值:

#include <utility>

inline int myGetPair1(int inp) {
    return 1023 + inp;
}

inline int myGetPair2(int inp) {
    return 31 + inp;
}

void getPair(const int inp, int& a, int& b) {
    a = myGetPair1(inp);
    b = myGetPair2(inp);
}
Run Code Online (Sandbox Code Playgroud)

)

但是,使用std :: pair会添加五个额外的汇编语句:

__Z7getPairiRiS_:
LFB18:
    leal    31(%rdi), %eax
    addl    $1023, %edi
    pushq   %rbp
LCFI0:
    salq    $32, %rax
    movq    %rsp, %rbp
LCFI1:
    orq %rdi, %rax
    movq    %rax, %rcx
    movl    %eax, (%rsi)
    shrq    $32, %rcx
    movl    %ecx, (%rdx)
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

其代码几乎与前面的示例一样简单:

#include <utility>

inline std::pair<int,int> myGetPair(int inp) {
    return std::make_pair(1023 + inp, 31 + inp);
}

void getPair(const int inp, int& a, int& b) {
    std::pair<int,int> result = myGetPair(inp);

    a = result.first;
    b = result.second;
}
Run Code Online (Sandbox Code Playgroud)

任何知道编译器内部工作原理的人都可以帮助解决这个问题吗?该升压元组页提到的性能损失的元组与传递通过引用,但没有链接文件的回答这个问题.

我喜欢的std ::对它们传递按引用语句的原因是,它使函数的意图多少清晰的在许多情况下,尤其是当其他参数的输入,以及将要被修改的.

Ale*_*tov 5

糟糕的优化.让我们希望有一天编译器会更好.


Pav*_*aev 5

我尝试使用VC++ 2008,使用cl.exe /c /O2 /FAs foo.cpp(即"仅编译且不链接","优化速度"和"在注释中使用匹配的源代码行转储汇编输出").这就是getLine()最终的结果.

"byref"版本:

PUBLIC  ?getPair@@YAXHAAH0@Z                ; getPair
; Function compile flags: /Ogtpy
;   COMDAT ?getPair@@YAXHAAH0@Z
_TEXT   SEGMENT
_inp$ = 8                       ; size = 4
_a$ = 12                        ; size = 4
_b$ = 16                        ; size = 4
?getPair@@YAXHAAH0@Z PROC               ; getPair, COMDAT

; 9    :     myGetPair(inp, a, b);

    mov eax, DWORD PTR _inp$[esp-4]
    mov edx, DWORD PTR _a$[esp-4]
    lea ecx, DWORD PTR [eax+1023]
    mov DWORD PTR [edx], ecx
    mov ecx, DWORD PTR _b$[esp-4]
    add eax, 31                 ; 0000001fH
    mov DWORD PTR [ecx], eax

; 10   : }

    ret 0
?getPair@@YAXHAAH0@Z ENDP               ; getPair
Run Code Online (Sandbox Code Playgroud)

" std::pairbyval " - 回归版:

PUBLIC  ?getPair@@YAXHAAH0@Z                ; getPair
; Function compile flags: /Ogtpy
;   COMDAT ?getPair@@YAXHAAH0@Z
_TEXT   SEGMENT
_inp$ = 8                       ; size = 4
_a$ = 12                        ; size = 4
_b$ = 16                        ; size = 4
?getPair@@YAXHAAH0@Z PROC               ; getPair, COMDAT

; 8    :     std::pair<int,int> result = myGetPair(inp);

    mov eax, DWORD PTR _inp$[esp-4]

; 9    : 
; 10   :     a = result.first;

    mov edx, DWORD PTR _a$[esp-4]
    lea ecx, DWORD PTR [eax+1023]
    mov DWORD PTR [edx], ecx

; 11   :     b = result.second;

    mov ecx, DWORD PTR _b$[esp-4]
    add eax, 31                 ; 0000001fH
    mov DWORD PTR [ecx], eax

; 12   : }

    ret 0
?getPair@@YAXHAAH0@Z ENDP               ; getPair
Run Code Online (Sandbox Code Playgroud)

如您所见,实际装配是相同的; 唯一的区别在于错误的名称和评论.