1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
// $Id: test_singleton.cpp 92386 2010-10-28 07:44:37Z johnnyw $
// This example illustrates the performance impact of using the
// Double-Checked Locking pattern compared with using the "standard"
// practice of acquiring and releasing a lock on every instance()
// call. In addition, we compare the performance of using the
// ACE_Singleton (which encapsulates the Double-Check Locking pattern)
// vs. hand-coding the pattern.
//
// Here's the output from running this test on our SPARCstation 20, model 712s.
//
// ./test_singleton 1000000
// iterations = 1000000
// ACE_Singleton
// real time = 0.193731 secs, user time = 0.190416 secs, system time = 0.000549 secs
// time per call = 0.193731 usecs
// DC_Singleton
// real time = 0.176208 secs, user time = 0.176045 secs, system time = 0.000092 secs
// time per call = 0.176208 usecs
// Mutex_Singleton
// real time = 3.160998 secs, user time = 3.121434 secs, system time = 0.000109 secs
// time per call = 3.160998 usecs
//
// As you can see, both Double-Checked Locking implementations are about
// 15 times faster than the standard mutex version. Moreover,
// this test is run with only a single thread, so there's no contention
// for the lock. If there were multiple threads contending for the lock,
// the Mutex_Singleton performance would get increasing worse...
#include "ace/OS_main.h"
#include "ace/Guard_T.h"
#include "ace/Profile_Timer.h"
#include "ace/Singleton.h"
#include "ace/Synch_Traits.h"
#include "ace/Log_Msg.h"
#include "test_singleton.h"
#if defined (ACE_HAS_THREADS)
static const int DEFAULT_ITERATIONS = 100000000;
class Mutex_Singleton
{
public:
Mutex_Singleton (void) {}
void svc (void) {}
static Mutex_Singleton *instance (void);
private:
static ACE_SYNCH_MUTEX lock_;
static Mutex_Singleton *instance_;
};
ACE_SYNCH_MUTEX Mutex_Singleton::lock_;
Mutex_Singleton *Mutex_Singleton::instance_;
Mutex_Singleton *
Mutex_Singleton::instance (void)
{
// Acquire the lock every time in.
ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, Mutex_Singleton::lock_, 0);
if (Mutex_Singleton::instance_ == 0)
ACE_NEW_RETURN (Mutex_Singleton::instance_, Mutex_Singleton, 0);
return Mutex_Singleton::instance_;
}
ACE_SYNCH_MUTEX DC_Singleton::lock_;
DC_Singleton *DC_Singleton::instance_;
DC_Singleton *
DC_Singleton::instance (void)
{
if (DC_Singleton::instance_ == 0)
{
// Only lock if instance_ isn't 0.
ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, DC_Singleton::lock_, 0);
// Perform the Double-Check.
if (DC_Singleton::instance_ == 0)
ACE_NEW_RETURN (DC_Singleton::instance_, DC_Singleton, 0);
}
return DC_Singleton::instance_;
}
typedef ACE_Singleton <DC_Singleton, ACE_SYNCH_MUTEX> My_Singleton;
int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
ACE_Profile_Timer timer;
int iterations = argc > 1 ? ACE_OS::atoi (argv[1]) : DEFAULT_ITERATIONS;
int i;
ACE_DEBUG ((LM_DEBUG, "iterations = %d\n", iterations));
// Test the ACE_Singleton performance (which uses Double-Checked
// Locking).
timer.start ();
for (i = 0; i < iterations; i++)
My_Singleton::instance ()->svc ();
timer.stop ();
ACE_Profile_Timer::ACE_Elapsed_Time et;
timer.elapsed_time (et);
ACE_DEBUG ((LM_DEBUG, "ACE_Singleton\n"));
ACE_DEBUG ((LM_DEBUG, "real time = %f secs, user time = %f secs, system time = %f secs\n",
et.real_time, et.user_time, et.system_time));
ACE_DEBUG ((LM_DEBUG, "time per call = %f usecs\n",
(et.real_time / double (iterations)) * 1000000));
// Test the hand-coded Singleton performance (which uses
// Double-Checked Locking).
timer.start ();
for (i = 0; i < iterations; i++)
DC_Singleton::instance ()->svc ();
timer.stop ();
timer.elapsed_time (et);
ACE_DEBUG ((LM_DEBUG, "DC_Singleton\n"));
ACE_DEBUG ((LM_DEBUG, "real time = %f secs, user time = %f secs, system time = %f secs\n",
et.real_time, et.user_time, et.system_time));
ACE_DEBUG ((LM_DEBUG, "time per call = %f usecs\n",
(et.real_time / double (iterations)) * 1000000));
// Test the Mutex_Singleton implementation (which does not use
// Double-Checked Locking).
timer.start ();
for (i = 0; i < iterations; i++)
Mutex_Singleton::instance ()->svc ();
timer.stop ();
timer.elapsed_time (et);
ACE_DEBUG ((LM_DEBUG, "Mutex_Singleton\n"));
ACE_DEBUG ((LM_DEBUG, "real time = %f secs, user time = %f secs, system time = %f secs\n",
et.real_time, et.user_time, et.system_time));
ACE_DEBUG ((LM_DEBUG, "time per call = %f usecs\n",
(et.real_time / double (iterations)) * 1000000));
return 0;
}
#if defined (ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION)
template ACE_Singleton<DC_Singleton, ACE_SYNCH_MUTEX> *
ACE_Singleton<DC_Singleton, ACE_SYNCH_MUTEX>::singleton_;
#endif /* ACE_HAS_EXPLICIT_STATIC_TEMPLATE_MEMBER_INSTANTIATION */
#else
int
ACE_TMAIN (int, ACE_TCHAR *[])
{
ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n"));
return 0;
}
#endif /* ACE_HAS_THREADS */
|