summaryrefslogtreecommitdiff
path: root/performance-tests/Misc/test_mutex.cpp
blob: 0f5a6bb8d810bd4f196dda0eccbc546e5893bd3f (plain)
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// This test program illustrates the performance difference between
// $Id$

// three versions of wrappers for thread mutexes.  These three
// versions exercise various combinations of the following classes:
// 
// Thread_Mutex --
//    This version is just like ACE_Thread_Mutex, which doesn't use
//    inheritance and dynamic binding.
// 
// Mutex_Base --
//    This is an abstract base class that defines the
//    acquire()/release() interface.
// 
// Thread_Mutex_Derived --
//    This derived from Mutex_Base and uses inheritance and
//    dynamic binding.
// 
// The following are the results I got when running this on our
// SPARCstation 20 model 712:
//
// ./test_mutex 1000000
// iterations = 1000000
// Thread_Mutex
// real time = 1.727843 secs, user time = 1.729262 secs, system time = 0.000325 secs
// time per call = 1.747843 usecs
// Thread_Mutex_Derived
// real time = 1.730225 secs, user time = 1.724744 secs, system time = 0.000096 secs
// time per call = 1.730225 usecs
// Mutex_Base
// real time = 2.112831 secs, user time = 2.104245 secs, system time = 0.000095 secs
// time per call = 2.112831 usecs
//
// My conclusions are as follows:
// 
// 1. If your C++ compiler optimizes calls to virtual functions that
//    are made through instances of derived classes, then the
//    performance of the Thread_Mutex and Thread_Mutex_Derived are
//    essentially identical.
// 
// 2. The overhead from using virtual functions is approximately
//    20%.  Naturally, as the amount of contention goes up, the
//    relative overhead of the virtual function calls will decrease.
//
// Keep in mind, however, that using virtual functions to implement
// the Thread_Mutex will make it infeasible to put instances of
// Thread_Mutex into shared memory since the vptrs won't point to the
// correct vtables...

#include "ace/Log_Msg.h"
#include "ace/Profile_Timer.h"
#include "ace/OS.h"

#if defined (ACE_HAS_THREADS)

static const int DEFAULT_ITERATIONS = 100000000;

// A thread mutex that doesn't use virtual functions.
class Thread_Mutex
{
public:
  Thread_Mutex (void);
  ~Thread_Mutex (void);
  int acquire (void);
  int release (void);

private:
  ACE_mutex_t mutex_;
};

Thread_Mutex::Thread_Mutex (void)
{
  ACE_OS::mutex_init (&this->mutex_);
}

Thread_Mutex::~Thread_Mutex (void)
{
  ACE_OS::mutex_destroy (&this->mutex_);
}

inline int
Thread_Mutex::acquire (void)
{
  return ACE_OS::mutex_lock (&this->mutex_);
}

inline int
Thread_Mutex::release (void)
{
  return ACE_OS::mutex_unlock (&this->mutex_);
}

// Base class for mutex, declares pure virtual functions.
class Mutex_Base
{
public:
  virtual int acquire (void) = 0;
  virtual int release (void) = 0;
};

// Subclass for threaded mutex, defines virtual functions.
class Thread_Mutex_Derived : public Mutex_Base
{
public:
  Thread_Mutex_Derived (void);
  ~Thread_Mutex_Derived (void);
  virtual int acquire (void);
  virtual int release (void);

private:
  ACE_mutex_t mutex_;
};

Thread_Mutex_Derived::Thread_Mutex_Derived (void)
{
  ACE_OS::mutex_init (&this->mutex_);
}

Thread_Mutex_Derived::~Thread_Mutex_Derived (void)
{
  ACE_OS::mutex_destroy (&this->mutex_);
}

inline int
Thread_Mutex_Derived::acquire (void)
{
  return ACE_OS::mutex_lock (&this->mutex_);
}

inline int
Thread_Mutex_Derived::release (void)
{
  return ACE_OS::mutex_unlock (&this->mutex_);
}

static Thread_Mutex thread_mutex;
static Thread_Mutex_Derived thread_mutex_derived;
static Mutex_Base *mutex_base = &thread_mutex_derived;

int 
main (int argc, char *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));

  timer.start ();

  // Test the thread mutex (which doesn't use inheritance or dynamic
  // binding).

  for (i = 0; i < iterations; i++)
    {
      thread_mutex.acquire ();
      thread_mutex.release ();
    }

  timer.stop ();

  ACE_Profile_Timer::ACE_Elapsed_Time et;

  timer.elapsed_time (et);

  ACE_DEBUG ((LM_DEBUG, "Thread_Mutex\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 thread mutex derived (which does use inheritance or
  // dynamic binding).  Note that we call this via an instance of the
  // derived class, so good C++ compilers should optimize the virtual
  // function calls in this case.

  timer.start ();

  for (i = 0; i < iterations; i++)
    {
      thread_mutex_derived.acquire ();
      thread_mutex_derived.release ();
    }

  timer.stop ();

  timer.elapsed_time (et);

  ACE_DEBUG ((LM_DEBUG, "Thread_Mutex_Derived\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 thread mutex derived (which does use inheritance or
  // dynamic binding).  Note that we call this via a pointer to the
  // base class, which points to an instance of the derived class.
  // Thus, C++ compilers won't be able to optimize the virtual
  // function calls in this case.

  timer.start ();

  for (i = 0; i < iterations; i++)
    {
      mutex_base->acquire ();
      mutex_base->release ();
    }

  timer.stop ();

  timer.elapsed_time (et);

  ACE_DEBUG ((LM_DEBUG, "Mutex_Base\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;
}
#else
int 
main (int, char *[])
{
  ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n"));
  return 0;
}
#endif /* ACE_HAS_THREADS */