16

My specific question is that when implementing a singleton class in C++, is there any substantial differences between the two below codes regarding performance, side issues or something:

class singleton
{
    // ...
    static singleton& getInstance()
    {
        // allocating on heap
        static singleton* pInstance = new singleton();
        return *pInstance;
    }
    // ...
};

and this:

class singleton
{
    // ...
    static singleton& getInstance()
    {
        // using static variable
        static singleton instance;
        return instance;
    }
    // ...
};


(Note that dereferencing in the heap-based implementation should not affect performance, as AFAIK there is no extra machine-code generated for dereferencing. It's seems only a matter of syntax to distinguish from pointers.)

UPDATE:

I've got interesting answers and comments which I try to summarize them here. (Reading detailed answers is recommended for those interested.)‎:

  • In the singleton using static local variable, the class destructor is automatically invoked at process termination, whereas in the dynamic allocation case, you have to manage object destruction someway at sometime, e.g. by using smart pointers:
    static singleton& getInstance() {
        static std::auto_ptr<singleton> instance (new singleton());
        return *instance.get(); 
    }
  • The singleton using dynamic allocation is "lazier" than the static singleton variable, as in the later case, the required memory for the singleton object is (always?) reserved at process start-up (as part of the whole memory required for loading program) and only calling of the singleton constructor is deferred to getInstance() call-time. This may matter when sizeof(singleton) is large.

  • Both are thread-safe in C++11. But with earlier versions of C++, it's implementation-specific.

  • The dynamic allocation case uses one level of indirection to access the singleton object, whereas in the static singleton object case, direct address of the object is determined and hard-coded at compile-time.


P.S.: I have corrected the terminology I'd used in the original posting according to the @TonyD's answer.

Masood Khaari
  • 2,420
  • 2
  • 20
  • 37
  • Have you compared the generated assembly for the two? – Some programmer dude Feb 25 '13 at 08:23
  • No. If you mean generated assembly for the two different implementations, they are obviously different, as one allocates on heap and one does a load/call time initialization. If you mean generated assembly for dereferencing, no, I haven't compared. I just guess so. – Masood Khaari Feb 25 '13 at 08:28

2 Answers2

7
  • the new version obviously needs to allocate memory at run-time, whereas the non-pointer version has the memory allocated at compile time (but both need to do the same construction)

  • the new version won't invoke the object's destructor at program termination, but the non-new version will: you could use a smart pointer to correct this

    • you need to be careful that some static/namespace-scope object's destructors don't invoke your singleton after its static local instance's destructor has run... if you're concerned about this, you should perhaps read a bit more about Singleton lifetimes and approaches to managing them. Andrei Alexandrescu's Modern C++ Design has a very readable treatment.
  • under C++03, it's implementation-defined whether either will be thread safe. (I believe GCC tends to be, whilst Visual Studio tends not -comments to confirm/correct appreciated.)

  • under C++11, it's safe: 6.7.4 "If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization." (sans recursion).

Discussion re compile-time versus run-time allocation & initialisation

From the way you've worded your summary and a few comments, I suspect you're not completely understanding a subtle aspect of the allocation and initialisation of static variables....

Say your program has 3 local static 32-bit ints - a, b and c - in different functions: the compiler's likely to compile a binary that tells the OS loader to leave 3x32-bits = 12 bytes of memory for those statics. The compiler decides what offsets each of those variables is at: it may put a at offset 1000 hex in the data segment, b at 1004, and c at 1008. When the program executes, the OS loader doesn't need to allocate memory for each separately - all it knows about is the total of 12 bytes, which it may or may not have been asked specifically to 0-initialise, but it may want to do anyway to ensure the process can't see left over memory content from other users' programs. The machine code instructions in the program will typically hard-code the offsets 1000, 1004, 1008 for accesses to a, b and c - so no allocation of those addresses is needed at run-time.

Dynamic memory allocation is different in that the pointers (say p_a, p_b, p_c) will be given addresses at compile time as just described, but additionally:

  • the pointed-to memory (each of a, b and c) has to be found at run-time (typically when the static function first executes but the compiler's allowed to do it earlier as per my comment on the other answer), and
    • if there's too little memory currently given to the process by the Operating System for the dynamic allocation to succeed, then the program library will ask the OS for more memory (e.g. using sbreak()) - which the OS will typically wipe out for security reasons
    • the dynamic addresses allocated for each of a, b and c have to be copied back into the pointers p_a, p_b and p_c.

This dynamic approach is clearly more convoluted.

Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
  • Nice points. By "memory allocation at compile time", do you mean that the required memory space is reserved at link- and load-time, but the initialization is deferred to the function call-time? (Otherwise your first point seems wrong) – Masood Khaari Feb 25 '13 at 08:47
  • I've just noticed [your quotation](http://stackoverflow.com/questions/15062767/heap-dynamic-vs-static-memory-allocation-for-c-singleton-class-instance/15063036#comment21179656_15062905) from C++11 6.7.4. But what about C++03 or earlier versions? – Masood Khaari Feb 25 '13 at 08:59
  • @MassoodKhaari: Re allocation: yes, the decisions re memory (amount needed, segment, offsets) for statics are made at compile time and the binary image will indicate enough about it (e.g. total size of memory region) for the OS loader to put the memory aside. Re concurrency - as per my answer - it's implementation defined (if at all)... the C++03 Standard did not mention threads at all, so it was up to the implementation to decide whether and how to support them. – Tony Delroy Feb 25 '13 at 09:09
  • C++03 is frightening. I summarized the answers altogether in the main post for easier tracking. – Masood Khaari Feb 25 '13 at 09:58
  • Thanks for clarification. But I already know what you explained. Maybe I haven't used correct words to convey my purpose which I hereby make clear... – Masood Khaari Feb 25 '13 at 13:05
  • By "static allocation", I don't mean that the OS does separately allocate memories for each static variable. But I mean the total memory that has to be considered for all the static variables which will be reserved at once at load time, as you explained. By "static initialization", I don't mean the initialization that OS does at load-time according to values set by compiler when compiling the program. But I mean calling class constructor when the C++ code for static initialization (like `static singleton instance` or the `new` one) executes. – Masood Khaari Feb 25 '13 at 13:06
  • 1
    @MassoodKhaari "By "static initialization", I don't mean [X, I mean Y"... the phrase can be confusing, but what you don't mean **is** known as "static initialization" by the C++ community, and your Y - "calling class constructor" - is termed as *dynamic* initialisation of a static variable. So, "static initialization" is not initialisation of a static ;-o. It's unfortunate `static` has so many meanings in C++ and computing science generally, allowing the terminology to be so fiddly. – Tony Delroy Mar 05 '14 at 14:27
  • :-) Yes, `static` can have so many interpretations in computer science, and to master the terminology of the C++ community I have to look for several advanced learning resources. Thanks for your care. – Masood Khaari Mar 06 '14 at 08:23
3

The main difference is that using a local static the object will be destroyed when closing the program, instead heap-allocated objects will just be abandoned without being destroyed.

Note that in C++ if you declare a static variable inside a function it will be initialized the first time you enter the scope, not at program start (like it happens instead for global static duration variables).

In general over the years I switched from using lazy initialization to explicit controlled initialization because program startup and shutdown are delicate phases and quite difficult to debug. If your class is not doing anything complex and just cannot fail (e.g. it's just a registry) then even lazy initialization is fine... otherwise being in control will save you quite a lot of problems.

A program that crashes before entering the first instruction of main or after executing last instruction of main is harder to debug.

Another problem of using lazy construction of singletons is that if your code is multithread you've to pay attention to the risk of having concurrent threads initializing the singleton at the same time. Doing initialization and shutdown in a single thread context is simpler.

The possible races during initialization of function-level static instances in multithreaded code has been resolved since C++11, when the language added official multithreading support: for normal cases proper synchronization guards are automatically added by the compiler so this is not a concern in C++11 or later code. However if initialization of a static in function a calls function b and vice-versa you can risk a deadlock if the two functions are called the first time at the same time by different threads (this is not an issue only if the compiler uses a single mutex for all statics). Note also that calling the function that contains a static object from within the initialization code of the static object recursively is not permitted.

6502
  • 104,192
  • 14
  • 145
  • 251
  • Yes, I am also using singletons for rather simple objects. But it seems interesting; can you mention some ways to implement the "explicit controlled initialization"? – Masood Khaari Feb 25 '13 at 08:39
  • And nice to point to the destruction issue. I hadn't noticed that. Therefore in this case, the static initialization seems a better choice. – Masood Khaari Feb 25 '13 at 08:42
  • "it will be initialized the first time you enter the scope" - sometimes / C++11 6.7.4: "An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization." – Tony Delroy Feb 25 '13 at 08:44
  • @MassoodKhaari: I simply mean that complex subsystems are initialized in a specific order in main and they're shut down in a specific order on exit. Things that happen before `main` starts or after `main` ends are sort of a gray area of the language (for example what standard library functions and what subsystems can you already/still use?). – 6502 Feb 25 '13 at 10:31