Search code examples
c++classstructinitializationmember-variables

What is performance difference between different class or struct initialization methods?


We have different types of initializing class or struct member variable at c++ one of them is:

struct foo {
    foo() : a(true), b(true), c(true) {}
    bool a;
    bool b;
    bool c;
 } bar;

and another one is :

struct foo {
    bool a = true;
    bool b = true;
    bool c = true;
 } bar;

Is there differences between them ?

which one is better to use ?


Solution

  • TL;DR Don't care about such trivialities unless you know you have to. And if you have to, you need to analyze concrete assembly.

    In general from pure language perspective such questions do not make sense. For what it's worth, c++ compiler can cripple performance if it wished. Any program exhibiting the same behaviour is just as legal, and performance is not a visible effect.

    Talking about performance makes sense only in concrete application that is concrete assembly using a particular compiler and configuration, especially optimizations.

    Full study: I will be using clag 7.0.0 on godbolt compiler explorer

    Your case is weird because it defines global values.For default optimization options:

    1. the c'tor was generated and object is stored as zeroes
    2. there was no code and the object was generate as 3 ones

    So clearly option 2 seems better:

    __cxx_global_var_init:                  # @__cxx_global_var_init
            push    rbp
            mov     rbp, rsp
            movabs  rdi, offset bar
            call    foo::foo() [base object constructor]
            pop     rbp
            ret
    foo::foo() [base object constructor]:  # @foo::foo() [base object constructor]
            push    rbp
            mov     rbp, rsp
            mov     qword ptr [rbp - 8], rdi
            mov     rdi, qword ptr [rbp - 8]
            mov     byte ptr [rdi], 1
            mov     byte ptr [rdi + 1], 1
            mov     byte ptr [rdi + 2], 1
            pop     rbp
            ret
    _GLOBAL__sub_I_example.cpp:             # @_GLOBAL__sub_I_example.cpp
            push    rbp
            mov     rbp, rsp
            call    __cxx_global_var_init
            pop     rbp
            ret
    bar:
            .zero   3
    
    bar2:
            .byte   1                       # 0x1
            .byte   1                       # 0x1
            .byte   1                       # 0x1
    

    However using -O1 reduces code to no difference:

    bar:
            .byte   1                       # 0x1
            .byte   1                       # 0x1
            .byte   1                       # 0x1
    
    bar2:
            .byte   1                       # 0x1
            .byte   1                       # 0x1
            .byte   1                       # 0x1
    

    When using fooas type in a program ctor is generated for both cases -O0 and -O1 (same ctor for each case, different per optimization level). See: https://godbolt.org/z/0il6ou

    And for -O2 the objects get dissolved.