Singleton速度比較 (3) ソースコード
(1) SynchronizedSingleton: 完全同期型Singleton
特に説明は不要でしょう。non-PODでstaticなオブジェクトを使用するのは好きじゃありませんが(メンバ変数m)、今回は目を瞑ります。
template<typename T> class SynchronizedSingleton : private boost::noncopyable { public: static T& getInstance(void) { boost::mutex::scoped_lock lk(m); if (!instance) { instance = new T; } return *instance; } private: static T* instance; static boost::mutex m; }; template<typename T> T* SynchronizedSingleton<T>::instance = 0; template<typename T> boost::mutex SynchronizedSingleton<T>::m;
(2) DCLSingleton
先に述べた方法で、x86用のメモリバリアを挿入し、安全なdouble checked lockingを実現したバージョンです。結果として、メモリバリアとして余計なインストラクションは挿入されず、g++の最適化を抑制するだけ("memory")になっているので速そうですね。
template<typename T> class DCLSingleton : private boost::noncopyable { public: static T& getInstance(void) { T* tmp = instance; __asm__ __volatile__ ( "" ::: "memory" ); // read memory barrier for x86 if (!instance) { boost::mutex::scoped_lock lk(m); if (!instance) { tmp = new T; __asm__ __volatile__ ( "" ::: "memory" ); // write memory barrier for x86 instance = tmp; } } return *instance; } private: static T* instance; static boost::mutex m; }; template<typename T> T* DCLSingleton<T>::instance = 0; template<typename T> boost::mutex DCLSingleton<T>::m;
(3) OnceSingleton
pthread_once()で実装したシングルトンです。それ自体はちょっと使い方がインタフェースが煩雑なので、内部でpthread_once()を使っているboost::call_once()で実装しました。少々手抜きですね。
// Once template<typename T> class OnceSingleton : private boost::noncopyable { public: static T& getInstance(void) { static boost::once_flag flg = BOOST_ONCE_INIT; boost::call_once(init, flg); return *instance; } private: static void init(void) { instance = new T; } static T* instance; }; template<typename T> T* OnceSingleton<T>::instance = 0;
(4) GccTSDSingleton
最近のGCCの__thread拡張を使ったシングルトンです。
// TSD (gcc拡張) template<typename T> class GccTSDSingleton : private boost::noncopyable { public: static T& getInstance(void) { static __thread T* tsd_instance = 0; if (!tsd_instance) { tsd_instance = getInstance_(); } return *tsd_instance; } private: static T* getInstance_(void) { boost::mutex::scoped_lock lk(m); if (!instance) { instance = new T; } return instance; } static T* instance; static boost::mutex m; }; template<typename T> T* GccTSDSingleton<T>::instance = 0; template<typename T> boost::mutex GccTSDSingleton<T>::m;
(5) TSDSingleton
POSIXで標準化されている、スレッド固有データを用いたシングルトンです。
こちらもboostで手抜きしたいところですが、Boost.ThreadにはTSD/TLSのAPIは(まだ)ないので、直接pthread APIを叩いています。。
// TSD (POSIX) template<typename T> class TSDSingleton : private boost::noncopyable { public: static T& getInstance(void) { static boost::once_flag flg = BOOST_ONCE_INIT; boost::call_once(init, flg); T* tsd_instance = static_cast<T*>(pthread_getspecific(key)); if (!tsd_instance) { tsd_instance = getInstance_(); pthread_setspecific(key, tsd_instance); } return *tsd_instance; } private: static void init(void) { pthread_key_create(&key, 0); } static T* getInstance_(void) { boost::mutex::scoped_lock lk(m); if (!instance) { instance = new T; } return instance; } static T* instance; static pthread_key_t key; static boost::mutex m; }; template<typename T> T* TSDSingleton<T>::instance = 0; template<typename T> pthread_key_t TSDSingleton<T>::key; template<typename T> boost::mutex TSDSingleton<T>::m;
次のコードでテストしました。
int main(void) { static const int TIMES = 500000000; // 5億回 double s; boost::timer t; t.restart(); for(int i = 0; i < TIMES; ++i) {} const double r = t.elapsed(); t.restart(); for(int i = 0; i < TIMES; ++i) { volatile X& x __attribute__((__unused__)) = DCLSingleton<X>::getInstance(); } s = t.elapsed() - r; std::cout << "DCLSingleton: " << s << " [s]" << std::endl; (略)
→続き