Good use of a Singleton?

Started by
28 comments, last by Sneftel 15 years, 11 months ago
A global variable is 'bad' when it is mutable and the behavior of unrelated portions of code rely on its state. In this respect, a logger is a 'good' global variable, because the behavior of code does not rely on the state of the logger even though it is mutable.

As for the four relevant questions to be asked when considering a singleton (including the one mentioned above), follow the "Singleton" link in my signature.
Advertisement
Quote:Original post by Sneftel
Quote:Original post by frob
That approach consumes substantially more memory.

How so? Each object consists of exactly one vtable pointer. How will having several pointers to one object be a win?

For this situation, lets assume some simple numbers:

vtable size: 80 bytes.
pointer size: 4 bytes.

In the first case of pointing to an immutable singleton, only one vtable exists. Every object requires a single pointer to the singleton. If we assume 255 actors, that is a total of 4*255 + 80*numstates bytes.

In the second case with each being unique instances, no static vtables exist. Again with 255 actors, we consume 80*255 bytes.

So (4*255 + 80*numstates) on one side, (80*255) on the other. As long as we have fewer than 242 states in the state machine, we have saved memory. In this particular case, we saved about 18KB. Multiply it by three interrelated state machines, and we save about 50KB. Consider the additional code cost for manipulating instances rather than assuming all unique addresses are unique states, and equivalent addresses are equivalent states, the additional creation and shutdown code, etc., and then you are up to around 100KB in combined code and data sizes.

So the choice is unique instances for about 100KB code+data, or singleton instances for about 3KB code+data.

As I said, on a modern PC the 100KB and small amount of overhead are not much. It is small, but they also aren't free, and certainly not "performance-ly" identical as you put it.

Unique instances require over 2% of the total memory on this system, whereas a collection of singletons requires 0.08% of the total memory. In this situation, singletons are clearly the better choice.
Objects don't contain vtables. They contain pointers to vtables. There is only one vtable for a class, no matter how many objects of that class you create. Try a sizeof if you don't believe me.
I use to love singletons. Now I hate them, because they make refactoring an utter nightmare. I find globals to be no better. If there is an excuse for globals, it's for that single application object, since if you go with more than one, you're entering the realm of undefined global initialization order.

The problem I have is even having a single global absolutely irks me, because now when I want to pull code that happens to use the logging facility into a unit test, I have to bring all this other crap along for the ride. So, let's forget about singletons for the moment and strictly talk globals. Am I alone here in thinking even an application object should not be global?
It's certainly possible to write an application that doesn't use globals, and that doesn't use big catchall pass-me-to-everything structs that pretend not to be full of globals. It is not necessarily easy, and the tradeoffs involved are not necessarily worth it. There's a widespread misconception these days that only object-oriented code is worth writing or capable of being maintainable and robust. This state of affairs helps cookie-cutter Java school graduates, OO textbook authors, and very few other people. In point of fact, a pure OO approach is usually not as quick to write or as easy to read as a blended approach, and (particularly for projects with less than 100,000 LoC or so) not necessarily ideal as a dominant paradigm.
Quote:Original post by Sneftel
Objects don't contain vtables. They contain pointers to vtables. There is only one vtable for a class, no matter how many objects of that class you create. Try a sizeof if you don't believe me.


Quoted for extreme emphasis. It's hard to believe how prevalent this misconception is. All kinds of people worry about the "overhead" of adding member functions when it simply isn't there.

You pay one pointer per object for the privilege of dynamic dispatch, and one vtable for the whole class, if any member functions use dynamic dispatch. Otherwise you pay nothing at all.
I'm another person who used to be quite fond of singleton classes (because of their convience) but I've grown to dislike them since veturing into writing multi-threaded code. I'm not one for getting too caught up in design critique; I tend to focus more on the practical reasons for adopting / not adopting a practical design, but I can tell you from experience you that singletons are utterly EVIL when it comes multithreaded code.

The reason they are evil is because singletons typically contain globals, and globals are very bad news for multi-threaded applications. Now of course you can put in safety locks / mutexes to ensure one thread has access at a time, but locks temporarily reduce the program to a single threaded application (when blocking) and you are not reaping the benefits of mutli-core if that is the case.

Of course there are exceptions to the 'singletons are bad' argument. If your class contains a read-only list of useful constants (C# uses such things a lot) then singletons are actually good. Other classes that do not neccesarily need to rely on global variables are also fine- such as factory classes for instance; as is the case in your (Manter's) example.

But other than that I'd say try to avoid them if at all possible.
Quote:Original post by Sneftel
It's certainly possible to write an application that doesn't use globals, and that doesn't use big catchall pass-me-to-everything structs that pretend not to be full of globals. It is not necessarily easy, and the tradeoffs involved are not necessarily worth it. There's a widespread misconception these days that only object-oriented code is worth writing or capable of being maintainable and robust. This state of affairs helps cookie-cutter Java school graduates, OO textbook authors, and very few other people. In point of fact, a pure OO approach is usually not as quick to write or as easy to read as a blended approach, and (particularly for projects with less than 100,000 LoC or so) not necessarily ideal as a dominant paradigm.


Hmm, interesting view. I tend to go mostly towards OO and template metaprogramming with an emphasis on extreme loose coupling and reusability. I'll admit that in some cases, the extra flexibility really didn't buy much. I guess one could make a case for Aspect-Oriented programming languages potentially solving some of these issues.
Quote:Original post by Sneftel
Objects don't contain vtables. They contain pointers to vtables. There is only one vtable for a class, no matter how many objects of that class you create. Try a sizeof if you don't believe me.

I believe you for any good compiler. Certainly it is true for any Windows compiler. It's a trivial optimization.

But it is also an optimization protected by a few patents in the United States (which is incredibly stupid) and one that quite a few embeded compilers don't support.

When I said 80 bytes, that is because it is what our system acutally uses.

As I almost mentioned, when you are are on a platform when you count your bytes and have to account for every byte in the executable, you pay attention. You also learn to appreciate a good optimizer, and really appreciate the good old days of having many megabytes of ram and running at gigahertz speeds. And you hate management for picking a C++ compiler in what should be a C environment.

/me mutters something about wishing he were back on a powerful platform...
Aspect-oriented programming is a little bit dead at this point, and in any case is neutered without OOP to back it up. No, I'm talking about plain old procedural (ooh, or functional) programming. You'd be amazed how complicated an application can be, yet be easy to implement without making a single class. I have to admit that I also have the habit of reaching for the metaprogramming swiss army knife far more than I should, but it's important to keep an eye on the tradeoffs of doing so. A great programmer whose identity eludes me once made the important observation that, left to their own devices, programmers will overcomplicate any application, simply due to the desire to make something great. It's a good instinct, but what makes an application great is whether the user's happy with it, not whether your AbstractStateMementoFactoryImpl is sufficiently generic.

This topic is closed to new replies.

Advertisement