Gaps in development, and some gaps in devblogs, but here we are anyway.
Let’s talk a little about optimising.
There are areas of the Argus code-base that I optimised the hell out of relatively early, and areas that haven’t gotten that kind of attention to-date. Why did some areas get done before others?
Either they were bits where the improvements they needed were obvious once I’d hammered out enough code, or they were places where the performance obviously sucked.
Performance suckage isn’t as obvious, until you start doing one thing a whole hell of a lot. Code can waste a ton of cycles, but if you only do it infrequently, it doesn’t matter so much if you do it efficiently or not.
What came up for me was a piece of old, old, old code. Something I sprayed into an editor on the first day of coding that has, over time, become an increasingly integral and frequently-used piece of code.
That is, Singletons.
The old code looked like this:
template<class T> class Singleton { protected: static T* self; static void ___CreateInstance() { self = new T; } public: static T* ptr() { if (!self) ___CreateInstance(); return self; } static T& ref() { if (!self) ___CreateInstance(); return *self; } virtual ~Singleton() { Singleton<T>::self = nullptr; }; Singleton() {}; Singleton(const Singleton&) = delete; // No copy constructor }; template<class T> T* Singleton<T>::self = nullptr;
Now, that’s all well and good. It was simple to use. Make it a base-class of a thing you want to be a Singleton, and then just access through: classname::ref().method()
Easy-peasy.
Also, however, slow, and not strictly-speaking thread-safe.
C++11 allows us to use a standards-defined gimmick with local static variables, and simplify this quite a bit.
template<class T> class Singleton_t { protected: Singleton_t() {}; public: inline static T& ref() { static T self; return self; } Singleton_t(const Singleton_t&) = delete; // No copy constructor Singleton_t(const Singleton_t&&) = delete; Singleton_t& operator= (const Singleton_t) = delete; };
That’s certainly a lot cleaner. It is, however, a bit wordier to use:
singleton_t<classname>::ref().method()
But how does it compare otherwise?
Well, it’s thread-safe (and we don’t need to bother with mutexes). C++11 guarantees we will never race on the initialisation of static T self.
Also, it’s fast. Bloody hell, but it is fast compared to the previous version.
I changed one Singleton class from the old model to the new model, and the performance improvement was so amazing, that I converted all of the rest of my singletons right away.
The end result is quite astonishing – though perhaps it shouldn’t be, because we access singletons like this a whole hell of a lot.
I’m well-pleased by this. So pleased that I also managed to get a few thousand words of a story-arc done, and ready to integrate with the main narrative.
More next time. And hopefully, next time will be sooner rather than later.