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;
(略)→続き