1

While reading the most upvoted question explaining the Singleton Pattern in C++, I realized some performance issues. The answer proposes the below form for implementing the pattern.

class SomeClass {
public: /** Singleton **/
    static SomeClass& instance() {
        static SomeClass singleInstance;

        return singleInstance;
    };

private: 
    SomeClass() = default;
    SomeClass(const SomeClass&) = delete;
};

When I implement the Singleton in the suggested form, the assembly of the program is as follows. Looking at the output below, I can easily say that the instance() method includes a branch to test whether it is the first call to the function or not (e.g. whether the static instance was created or not).

SomeClass::instance():
        push    rbp
        mov     rbp, rsp
        movzx   eax, BYTE PTR guard variable for SomeClass::instance()::singleInstance[rip]
        test    al, al
        sete    al
        test    al, al
        je      .L2
        mov     edi, OFFSET FLAT:guard variable for SomeClass::instance()::singleInstance
        call    __cxa_guard_acquire
        test    eax, eax
        setne   al
        test    al, al
        je      .L2
        mov     edi, OFFSET FLAT:guard variable for SomeClass::instance()::singleInstance
        call    __cxa_guard_release
.L2:
        mov     eax, OFFSET FLAT:SomeClass::instance()::singleInstance
        pop     rbp
        ret

Then, I tried to separate the declaration of the static object from the static instance() method like below.

class SomeClass {
public: /** Singleton **/
    static SomeClass& instance() {
        return singleInstance;
    };

private: 
    SomeClass() = default;
    SomeClass(const SomeClass&) = delete;
    
    static SomeClass singleInstance;
};

SomeClass SomeClass::singleInstance; // In the source file

The output of the second program is, as expected, different from the first one. It doesn't include a branch that tests the existence of the Singleton object.

SomeClass::instance():
        push    rbp
        mov     rbp, rsp
        mov     eax, OFFSET FLAT:SomeClass::singleInstance
        pop     rbp
        ret

I've found a similar question explaining the reason for using the in-class static definition of the Singleton instance.

Is it really better to declare the Singleton object outside of the static getter function if the static initialization order isn't a problem?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Caglayan DOKME
  • 948
  • 8
  • 21
  • Given that, at the time of writing, a CPU will execute billions of instructions per second, I'm not sure the perceived overhead of a branch (which a sensible branch predictor will obviate), will ever persuade over the advantages of localising the `static`. Good analysis and question though, +1. – Bathsheba Mar 16 '22 at 10:04
  • @Bathsheba Thanks for the appreciation. I've linked a similar question with explanatory answers. Looks like the solution is highly dependent on the case. If the Singleton objects are referring to each other, then the [static initialization fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order) might occur. By the way, I'm a guy who tries save each and every cycle on embedded systems :) – Caglayan DOKME Mar 16 '22 at 10:11

0 Answers0