Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ std::string initialization better performance (assembly)

I was playing with www.godbolt.org to check what code generates better assembly code, and I can't understand why this two different approaches generate different results (in assembly commands).

The first approach is to declare a string, and then later set a value:

#include <string>
int foo() {
    std::string a;
    a = "abcdef";
    return a.size();
}

Which, in my gcc 7.4 (-O3) outputs:

.LC0:
        .string "abcdef"
foo():
        push    rbp
        mov     r8d, 6
        mov     ecx, OFFSET FLAT:.LC0
        xor     edx, edx
        push    rbx
        xor     esi, esi
        sub     rsp, 40
        lea     rbx, [rsp+16]
        mov     rdi, rsp
        mov     BYTE PTR [rsp+16], 0
        mov     QWORD PTR [rsp], rbx
        mov     QWORD PTR [rsp+8], 0
        call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
        mov     rdi, QWORD PTR [rsp]
        mov     rbp, QWORD PTR [rsp+8]
        cmp     rdi, rbx
        je      .L1
        call    operator delete(void*)
.L1:
        add     rsp, 40
        mov     eax, ebp
        pop     rbx
        pop     rbp
        ret
        mov     rbp, rax
        jmp     .L3
foo() [clone .cold]:
.L3:
        mov     rdi, QWORD PTR [rsp]
        cmp     rdi, rbx
        je      .L4
        call    operator delete(void*)
.L4:
        mov     rdi, rbp
        call    _Unwind_Resume

So, I imagined that if I initialize the string in the declaration, the output assembly would be shorter:

int bar() {
    std::string a {"abcdef"};
    return a.size();
}

And indeed it is:

bar():
        mov     eax, 6
        ret

Why this huge difference? What prevents gcc to optimize the first version similar to the second?

godbolt link

like image 787
Eduardo Fernandes Avatar asked Dec 13 '19 18:12

Eduardo Fernandes


1 Answers

This is just a guess:

operator= has a strong exception guarantee; which means:

If an exception is thrown for any reason, this function has no effect (strong exception guarantee). (since C++11)

(source)

So while the constructor can leave the object in any condition it likes, operator= needs to make sure that the object is the same as before; I suspect that's why the call to operator delete is there (to clean up potentially allocated memory).

like image 140
Daniel Jour Avatar answered Oct 06 '22 01:10

Daniel Jour