summaryrefslogtreecommitdiff
path: root/trunk/ACE/performance-tests/Misc/context_switch_time.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/ACE/performance-tests/Misc/context_switch_time.cpp')
-rw-r--r--trunk/ACE/performance-tests/Misc/context_switch_time.cpp1318
1 files changed, 1318 insertions, 0 deletions
diff --git a/trunk/ACE/performance-tests/Misc/context_switch_time.cpp b/trunk/ACE/performance-tests/Misc/context_switch_time.cpp
new file mode 100644
index 00000000000..6844f20e2d2
--- /dev/null
+++ b/trunk/ACE/performance-tests/Misc/context_switch_time.cpp
@@ -0,0 +1,1318 @@
+// $Id$
+
+// ============================================================================
+//
+// = LIBRARY
+// (none)
+//
+// = FILENAME
+// context_switch_time.cpp
+//
+// = DESCRIPTION
+// Program that calculates context switch time between threads.
+// The Suspend-Resume test is based on the Task Context Switching
+// measurement approach described in:
+// Darren Cathey<br>
+// "RTOS Benchmarking -- All Things Considered . . ."<br>
+// <a href="http://www.realtime-info.be"><em>Real-Time Magazine</em></a>,
+// Second Quarter 1993,
+// <em>reprinted by <a href="http://www.wrs.com/artreqfm.html">Wind River
+// Systems</a></em><p>
+// which in turn is based on Superconducting Super Collider (SSC)
+// Laboratory Ping Suspend/Resume Task and Suspend/Resume Task benchmarks.
+// It measures two different times:
+// 1) The time to resume a blocked high priority task, which does
+// nothing other than block immediately. A lower priority task
+// resumes the high priority task, so the elapsed time includes
+// two context switches, one task suspend, and one task resume.
+// 2) The time to suspend and resume a low priority task that does
+// nothing. There is no context switching. This time is subtracted
+// from the one described in 1) above, and the result is divided by
+// two to yield the context switch time.
+//
+// Notes:
+// On Solaris 2.5.1, it appears that the lowest context switching times,
+// at least on a single-CPU machine, are obtained _without_ creating new
+// LWPs for new threads (THR_NEW_LWP). The -n option enables the use of
+// THR_NEW_LWP for testing.
+//
+// = CREATION DATE
+// 17 January 1997
+//
+// = AUTHOR
+// David L. Levine
+//
+// ============================================================================
+
+static const char usage [] = "[-? |\n"
+ " [-c <repeat counter, 0 means forever>]\n"
+ " [-n to spawn a new LWP with each thread\n"
+ "[<iterations>]]";
+
+#include "ace/OS_NS_stdio.h"
+#include "ace/OS_main.h"
+#include "ace/Task.h"
+#include "ace/Sched_Params.h"
+#include "ace/Stats.h"
+#include "ace/High_Res_Timer.h"
+#include "ace/Get_Opt.h"
+#include "ace/Thread_Semaphore.h"
+#include "ace/Barrier.h"
+#include "ace/OS_NS_unistd.h"
+#include "ace/OS_NS_errno.h"
+
+ACE_RCSID(Misc, context_switch_time, "$Id$")
+
+#if defined (ACE_HAS_THREADS)
+
+#if !defined (ACE_DEBUG_CST)
+# define ACE_DEBUG_CST 0
+#endif /* ACE_DEBUG_CST */
+
+static const u_int LOW_PRIORITY = ACE_THR_PRI_FIFO_DEF;
+static u_int HIGH_PRIORITY;
+
+// Global test configuration parameters.
+static ACE_UINT32 counter = 1;
+static ACE_UINT32 num_iterations = 1000;
+static ACE_UINT32 new_lwp = 0;
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Low_Priority_Null_Task
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Low_Priority_Null_Task : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ Low_Priority_Null_Task ();
+ virtual ~Low_Priority_Null_Task ();
+
+ virtual int svc ();
+
+ // Called by other task: it returns when this task is ready to
+ // continue.
+ void ready () { initialized_.acquire (); }
+
+ void done ();
+
+ ACE_hthread_t thread_id () const { return thread_id_; }
+private:
+ ACE_hthread_t thread_id_;
+ ACE_Semaphore initialized_; // Blocks until thread_id_ is assigned.
+ ACE_Semaphore blocked_semaphore_;
+
+ // Force proper construction of independent instances.
+ Low_Priority_Null_Task (const Low_Priority_Null_Task &);
+ Low_Priority_Null_Task &operator= (const Low_Priority_Null_Task &);
+};
+
+inline
+Low_Priority_Null_Task::Low_Priority_Null_Task() :
+ ACE_Task<ACE_MT_SYNCH> (ACE_Thread_Manager::instance ()),
+ initialized_ (0), // initialize to locked, then unlock when ready
+ blocked_semaphore_ (0)
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Low_Priority_Null_Task ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, LOW_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Low_Priority_Null_Task ctor, activated\n"));
+#endif /* ACE_DEBUG_CST */
+}
+
+Low_Priority_Null_Task::~Low_Priority_Null_Task()
+{
+}
+
+int
+Low_Priority_Null_Task::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Low_Priority_Null_Task::svc (), entering"));
+#endif /* ACE_DEBUG_CST */
+
+ ACE_Thread_Manager::instance ()->thr_self (thread_id_);
+ initialized_.release ();
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u\n", thread_id_));
+#endif /* ACE_DEBUG_CST */
+
+ // This task must never actually execute, so just have it block
+ // on a semaphore forever . . .
+ blocked_semaphore_.acquire ();
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Low_Priority_Task::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+void
+Low_Priority_Null_Task::done ()
+{
+ blocked_semaphore_.release ();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Suspend_Resume_Test
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Suspend_Resume_Test : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ Suspend_Resume_Test (const ACE_UINT32 iterations);
+ virtual ~Suspend_Resume_Test ();
+
+ virtual int svc ();
+
+ ACE_hrtime_t elapsed_time () const { return elapsed_time_; }
+private:
+ const ACE_UINT32 iterations_;
+
+ Low_Priority_Null_Task low_;
+
+ ACE_High_Res_Timer timer_;
+
+ ACE_hrtime_t elapsed_time_;
+
+ // Force proper construction of independent instances.
+ Suspend_Resume_Test ();
+ Suspend_Resume_Test (const Suspend_Resume_Test &);
+ Suspend_Resume_Test &operator= (const Suspend_Resume_Test &);
+};
+
+Suspend_Resume_Test::Suspend_Resume_Test (const ACE_UINT32 iterations) :
+ ACE_Task<ACE_MT_SYNCH> (),
+ iterations_ (iterations),
+ low_ (),
+ timer_ ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Suspend_Resume_Test ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, HIGH_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+}
+
+Suspend_Resume_Test::~Suspend_Resume_Test()
+{
+}
+
+int
+Suspend_Resume_Test::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_hthread_t thread_id;
+ ACE_Thread_Manager::instance ()->thr_self (thread_id);
+
+ ACE_DEBUG ((LM_DEBUG, "Suspend_Resume_Test::svc (), thread ID is %d\n",
+ thread_id));
+#endif /* ACE_DEBUG_CST */
+
+ low_.ready ();
+
+ // For information: the cost of the just the loop itself below,
+ // without the suspend and resume calls, on a 166 MHz Ultrasparc
+ // is about 12.3 nanoseconds per iteration.
+
+ timer_.start ();
+
+ for (ACE_UINT32 i = 0; i < iterations_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0)
+ ACE_DEBUG ((LM_DEBUG, "Suspend_Resume_Test::svc (), iteration %u\n",
+ i));
+#endif /* ACE_DEBUG_CST */
+
+ if (ACE_OS::thr_suspend (low_.thread_id ()) != 0)
+ {
+ ACE_ERROR ((LM_ERROR, "%p\n", "thr_suspend"));
+ low_.done ();
+ return -1;
+ }
+
+ if (ACE_OS::thr_continue (low_.thread_id ()) != 0 &&
+ errno != EINVAL)
+ // EINVAL is OK: it just means that the thread needs to be joined.
+ {
+ ACE_ERROR ((LM_ERROR, "%p\n", "thr_continue"));
+ low_.done ();
+ return -1;
+ }
+ }
+
+ timer_.stop ();
+ timer_.elapsed_microseconds (elapsed_time_);
+
+ low_.done ();
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Suspend_Resume_Test::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class High_Priority_Simple_Task
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class High_Priority_Simple_Task : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ High_Priority_Simple_Task ();
+ virtual ~High_Priority_Simple_Task ();
+
+ virtual int svc ();
+
+ // called by other task: it returns when this task is ready to
+ // continue
+ void ready () { initialized_.acquire (); }
+
+ void done ();
+
+ ACE_hthread_t thread_id () const { return thread_id_; }
+ ACE_UINT32 iterations () const { return iterations_; }
+private:
+ ACE_hthread_t thread_id_;
+ ACE_Semaphore initialized_; // Block until thread_id_ is assigned.
+ int terminate_;
+ ACE_UINT32 iterations_;
+
+ // Force proper construction of independent instances.
+ High_Priority_Simple_Task (const High_Priority_Simple_Task &);
+ High_Priority_Simple_Task &operator= (const High_Priority_Simple_Task &);
+};
+
+inline
+High_Priority_Simple_Task::High_Priority_Simple_Task() :
+ ACE_Task<ACE_MT_SYNCH> (ACE_Thread_Manager::instance ()),
+ initialized_ (0), // Initialize to locked, then unlock when ready.
+ terminate_ (0),
+ iterations_ (0)
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, HIGH_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task ctor, activated\n"));
+#endif /* ACE_DEBUG_CST */
+}
+
+High_Priority_Simple_Task::~High_Priority_Simple_Task()
+{
+}
+
+int
+High_Priority_Simple_Task::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task::svc (), entering"));
+#endif /* ACE_DEBUG_CST */
+
+ ACE_Thread_Manager::instance ()->thr_self (thread_id_);
+ initialized_.release ();
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u\n", thread_id_));
+#endif /* ACE_DEBUG_CST */
+
+ for (ACE_UINT32 i = 0; ! terminate_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task::svc, suspend self ("
+ "%u)\n", thread_id_));
+#endif /* ACE_DEBUG_CST */
+
+ ++iterations_;
+
+ // immediately suspend self
+ if (ACE_OS::thr_suspend (thread_id_) != 0)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "thr_suspend"), -1);
+ }
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task::svc, resumed (%u)\n",
+ thread_id_));
+#endif /* ACE_DEBUG_CST */
+ }
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Simple_Task::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+inline
+void
+High_Priority_Simple_Task::done ()
+{
+ terminate_ = 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Ping_Suspend_Resume_Test
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Ping_Suspend_Resume_Test : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ Ping_Suspend_Resume_Test (const ACE_UINT32 iterations);
+ virtual ~Ping_Suspend_Resume_Test ();
+
+ virtual int svc ();
+
+ ACE_hrtime_t elapsed_time () const { return elapsed_time_; }
+private:
+ const ACE_UINT32 iterations_;
+
+ High_Priority_Simple_Task high_;
+
+ ACE_High_Res_Timer timer_;
+
+ ACE_hrtime_t elapsed_time_;
+
+ // Force proper construction of independent instances.
+ Ping_Suspend_Resume_Test ();
+ Ping_Suspend_Resume_Test (const Ping_Suspend_Resume_Test &);
+ Ping_Suspend_Resume_Test &operator= (const Ping_Suspend_Resume_Test &);
+};
+
+Ping_Suspend_Resume_Test::Ping_Suspend_Resume_Test (
+ const ACE_UINT32 iterations)
+:
+ ACE_Task<ACE_MT_SYNCH> (),
+ iterations_ (iterations),
+ high_ (),
+ timer_ ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, LOW_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+}
+
+Ping_Suspend_Resume_Test::~Ping_Suspend_Resume_Test()
+{
+}
+
+int
+Ping_Suspend_Resume_Test::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test::svc (), entering"));
+
+ ACE_hthread_t thread_id;
+ ACE_Thread_Manager::instance ()->thr_self (thread_id);
+
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u\n", thread_id));
+#endif /* ACE_DEBUG_CST */
+
+ high_.ready ();
+
+#if ACE_DEBUG_CST > 0
+ int priority, high_priority;
+ ACE_OS::thr_getprio (thread_id, priority);
+ ACE_OS::thr_getprio (high_.thread_id (), high_priority);
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test::svc (), priority is %d, "
+ ", high thread priority is %d\n",
+ priority, high_priority));
+#endif /* ACE_DEBUG_CST */
+
+ // For information: the cost of the just the loop itself below,
+ // without the suspend and resume calls, on a 166 MHz Ultrasparc
+ // is about 12.3 nanoseconds per iteration.
+
+ timer_.start ();
+
+ ACE_UINT32 i;
+
+ for (i = 0; i < iterations_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0)
+ {
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test::svc (), iteration "
+ "%d, continue high-priority thread %u\n",
+ i, high_.thread_id ()));
+ }
+#endif /* ACE_DEBUG_CST */
+ if (ACE_OS::thr_continue (high_.thread_id ()) != 0 &&
+ errno != EINVAL)
+ // EINVAL is OK: it just means that the thread needs to be joined.
+ {
+ ACE_ERROR ((LM_ERROR, "%p\n", "thr_continue"));
+ high_.done ();
+ return -1;
+ }
+ }
+
+ timer_.stop ();
+ timer_.elapsed_microseconds (elapsed_time_);
+
+ high_.done ();
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test::svc: told high priority "
+ "task to terminate\n"));
+#endif /* ACE_DEBUG_CST */
+
+ // Resume the thread until thr_continue fails, indicating that it has
+ // finished.
+ for (i = 0; i < 10000 && ! ACE_OS::thr_continue (high_.thread_id ());
+ ++i) /* null */;
+
+ // Don't count the one iteration that was used to allow the high-priority
+ // thread to terminate.
+ if (high_.iterations () < iterations_)
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test: high priority task "
+ "executed only %u iterations!\n",
+ high_.iterations ()));
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Ping_Suspend_Resume_Test::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Yield_Test
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Yield_Test : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ Yield_Test (const ACE_UINT32 iterations);
+ virtual ~Yield_Test ();
+
+ virtual int svc ();
+
+ ACE_hrtime_t elapsed_time () const { return elapsed_time_; }
+private:
+ const ACE_UINT32 iterations_;
+#if defined (VXWORKS)
+ ACE_Thread_Mutex mutex_;
+ u_int started_;
+ u_int stopped_;
+#else /* ! VXWORKS */
+ ACE_Barrier timer_barrier_;
+#endif /* ! VXWORKS */
+ ACE_High_Res_Timer timer_;
+ ACE_hrtime_t elapsed_time_;
+
+ // Force proper construction of independent instances.
+ Yield_Test ();
+ Yield_Test (const Yield_Test &);
+ Yield_Test &operator= (const Yield_Test &);
+};
+
+Yield_Test::Yield_Test (const ACE_UINT32 iterations) :
+ ACE_Task<ACE_MT_SYNCH> (),
+ iterations_ (iterations),
+#if defined (VXWORKS)
+ mutex_ (),
+ started_ (0),
+ stopped_ (0),
+#else /* ! VXWORKS */
+ timer_barrier_ (3),
+#endif /* ! VXWORKS */
+ timer_ ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Yield_Test ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+#if !defined (VXWORKS)
+ timer_.start ();
+#endif /* ! VXWORKS */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 2, 0, LOW_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+
+#if !defined (VXWORKS)
+ timer_barrier_.wait ();
+ timer_.stop ();
+#endif /* ! VXWORKS */
+
+ timer_.elapsed_microseconds (elapsed_time_);
+}
+
+Yield_Test::~Yield_Test()
+{
+}
+
+int
+Yield_Test::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Yield_Test::svc (), entering"));
+
+ ACE_hthread_t thread_id;
+ ACE_Thread_Manager::instance ()->thr_self (thread_id);
+
+ int priority;
+ ACE_OS::thr_getprio (thread_id, priority);
+
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u, priority is %u\n", thread_id,
+ priority));
+#endif /* ACE_DEBUG_CST */
+
+#if defined (VXWORKS)
+ // Start the timer, if it hasn't already been started.
+ if (! started_)
+ {
+ // Double-check.
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, mutex_, -1);
+
+ if (! started_)
+ {
+ started_ = 1;
+ timer_.start ();
+ }
+ }
+#endif /* VXWORKS */
+
+ for (ACE_UINT32 i = 0; i < iterations_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0)
+ {
+ ACE_DEBUG ((LM_DEBUG, "Yield_Test::svc () [%u], iteration %u\n",
+ thread_id, i));
+ }
+#endif /* ACE_DEBUG_CST */
+
+ ACE_OS::thr_yield ();
+ }
+
+#if defined (VXWORKS)
+ // Stop the timer, if it hasn't already been started.
+ if (! stopped_)
+ {
+ // Maybe it would be better to read the clock before grabbing
+ // the mutex. Then, only apply the clock reading below, instead
+ // of reading the clock after grabbing the mutex.
+
+ // Double-check.
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, mutex_, -1);
+
+ if (! stopped_)
+ {
+ stopped_ = 1;
+ timer_.stop ();
+ timer_.elapsed_time (elapsed_time_); /* nanoseconds */
+ }
+ }
+#else /* ! VXWORKS */
+ timer_barrier_.wait ();
+#endif /* ! VXWORKS */
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Yield_Test::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Mutex_Acquire_Release_Test
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Mutex_Acquire_Release_Test
+{
+public:
+ Mutex_Acquire_Release_Test (const ACE_UINT32 iterations);
+ virtual ~Mutex_Acquire_Release_Test ();
+
+ virtual int svc ();
+
+ ACE_hrtime_t elapsed_time () const { return elapsed_time_; }
+private:
+ ACE_Thread_Mutex mutex_;
+ // Mutex used for acquire/release time measurement.
+
+ ACE_Thread_Semaphore sem_;
+ // Semaphore used for acquire/release time measurement.
+
+ const ACE_UINT32 iterations_;
+
+ ACE_High_Res_Timer timer_;
+
+ ACE_hrtime_t elapsed_time_;
+
+ // Force proper construction of independent instances.
+ Mutex_Acquire_Release_Test ();
+ Mutex_Acquire_Release_Test (const Mutex_Acquire_Release_Test &);
+ Mutex_Acquire_Release_Test &operator= (const Mutex_Acquire_Release_Test &);
+};
+
+Mutex_Acquire_Release_Test::Mutex_Acquire_Release_Test (
+ const ACE_UINT32 iterations) :
+ mutex_ (),
+ sem_ (),
+ iterations_ (iterations),
+ timer_ ()
+{
+}
+
+Mutex_Acquire_Release_Test::~Mutex_Acquire_Release_Test()
+{
+}
+
+int
+Mutex_Acquire_Release_Test::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_hthread_t thread_id;
+ ACE_Thread_Manager::instance ()->thr_self (thread_id);
+
+ ACE_DEBUG ((LM_DEBUG,
+ "Mutex_Acquire_Release_Test::svc (), thread ID is %d\n",
+ thread_id));
+#endif /* ACE_DEBUG_CST */
+
+ timer_.start ();
+
+ for (ACE_UINT32 i = 0; i < iterations_; ++i)
+ {
+ // Block on the mutex.
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, mutex_, -1);
+
+ // Release the mutex so that the low priority thread can
+ // proceed. The ACE_GUARD_RETURN macro implicity releases the
+ // mutex.
+ }
+
+ timer_.stop ();
+ timer_.elapsed_time (elapsed_time_); /* nanoseconds */
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Mutex_Acquire_Release_Test::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class High_Priority_Synchronized_Task
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class High_Priority_Synchronized_Task : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ High_Priority_Synchronized_Task (ACE_Thread_Semaphore &sem,
+ ACE_Thread_Mutex &mutex,
+ ACE_High_Res_Timer &timer);
+ virtual ~High_Priority_Synchronized_Task ();
+
+ virtual int svc ();
+
+ void ready () { initialized_.acquire (); }
+ // Called by other task: it returns when this task is ready to
+ // continue
+
+ void done ();
+
+ ACE_UINT32 average_context_switch_time () const;
+
+ ACE_hthread_t thread_id () const { return thread_id_; }
+ ACE_UINT32 iterations () const { return iterations_; }
+private:
+ ACE_hthread_t thread_id_;
+ ACE_Semaphore initialized_; // Block until thread_id_ is assigned.
+ int terminate_;
+ ACE_UINT32 iterations_;
+
+ ACE_Thread_Semaphore &sem_;
+ // Semaphore used to resume the task.
+
+ ACE_Thread_Mutex &mutex_;
+ // Mutex used to block the task.
+
+ ACE_High_Res_Timer &timer_;
+ // Clock shared between low and high priority tasks.
+
+ ACE_hrtime_t total_time_;
+ // Running total context switch time, nsec.
+
+ // Force proper construction of independent instances.
+ High_Priority_Synchronized_Task ();
+ High_Priority_Synchronized_Task (const High_Priority_Synchronized_Task &);
+ High_Priority_Synchronized_Task &
+ operator= (const High_Priority_Synchronized_Task &);
+};
+
+High_Priority_Synchronized_Task::High_Priority_Synchronized_Task (
+ ACE_Thread_Semaphore &sem,
+ ACE_Thread_Mutex &mutex,
+ ACE_High_Res_Timer &timer) :
+ ACE_Task<ACE_MT_SYNCH> (ACE_Thread_Manager::instance ()),
+ initialized_ (0), // Initialize to locked, then unlock when ready.
+ terminate_ (0),
+ iterations_ (0),
+ sem_ (sem),
+ mutex_ (mutex),
+ timer_ (timer),
+ total_time_ (0)
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Synchronized_Task ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, HIGH_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Synchronized_Task ctor, activated\n"));
+#endif /* ACE_DEBUG_CST */
+}
+
+High_Priority_Synchronized_Task::~High_Priority_Synchronized_Task()
+{
+}
+
+int
+High_Priority_Synchronized_Task::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Synchronized_Task::svc (), entering"));
+#endif /* ACE_DEBUG_CST */
+
+ ACE_Thread_Manager::instance ()->thr_self (thread_id_);
+
+ ACE_UINT32 mutex_acquire_release_time = 0;
+ {
+ Mutex_Acquire_Release_Test mutex_acquire_release_test (num_iterations);
+ mutex_acquire_release_test.svc ();
+ mutex_acquire_release_time =
+ static_cast<ACE_UINT32> (mutex_acquire_release_test.elapsed_time () /
+ num_iterations);
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "mutex_acquire_release: %u nsec\n",
+ mutex_acquire_release_time));
+#endif /* ACE_DEBUG_CST */
+ }
+
+ initialized_.release ();
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u\n", thread_id_));
+#endif /* ACE_DEBUG_CST */
+
+ for (ACE_UINT32 i = 0; ! terminate_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG,
+ "High_Priority_Synchronized_Task::svc, wait on sem ("
+ "%u)\n", thread_id_));
+#endif /* ACE_DEBUG_CST */
+
+ if (sem_.acquire () != 0)
+ {
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "sem_.acquire"), -1);
+ }
+
+ {
+ // Block on the mutex.
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, guard, mutex_, -1);
+
+ timer_.stop ();
+
+ ++iterations_;
+
+ ACE_hrtime_t nsec;
+ timer_.elapsed_time (nsec);
+ const ACE_UINT32 context_switch_time =
+ ACE_U64_TO_U32 (nsec) >= mutex_acquire_release_time ?
+ ACE_U64_TO_U32 (nsec) - mutex_acquire_release_time : 0;
+
+ total_time_ += context_switch_time;
+
+ // Release the mutex so that the low priority thread can
+ // proceed. The ACE_GUARD_RETURN macro implicity releases the
+ // mutex.
+ }
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG,
+ "High_Priority_Synchronized_Task::svc, resumed (%u)\n",
+ thread_id_));
+#endif /* ACE_DEBUG_CST */
+ }
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "High_Priority_Synchronized_Task::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+inline
+void
+High_Priority_Synchronized_Task::done ()
+{
+ terminate_ = 1;
+}
+
+ACE_UINT32
+High_Priority_Synchronized_Task:: average_context_switch_time () const
+{
+ return iterations_ > 0 ? static_cast<ACE_UINT32> (total_time_ / iterations_)
+ : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// class Synchronized_Suspend_Resume_Test
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+class Synchronized_Suspend_Resume_Test : public ACE_Task<ACE_MT_SYNCH>
+{
+public:
+ Synchronized_Suspend_Resume_Test (const ACE_UINT32 iterations);
+ virtual ~Synchronized_Suspend_Resume_Test ();
+
+ virtual int svc ();
+
+ ACE_UINT32 average_context_switch_time ();
+
+ ACE_hrtime_t elapsed_time () const { return elapsed_time_; }
+private:
+ const ACE_UINT32 iterations_;
+
+ ACE_Thread_Semaphore sem_;
+ // Used by the low priority thread to resume the high priority thread.
+
+ ACE_Thread_Mutex mutex_;
+ // Used by the low priority thread to block the high priority thread.
+
+ ACE_High_Res_Timer timer_;
+ // Clock shared between low and high priority tasks.
+
+ High_Priority_Synchronized_Task high_;
+ // The high priority task.
+
+ ACE_hrtime_t elapsed_time_;
+
+ ACE_UINT32 mutex_acquire_release_time_;
+
+ // Force proper construction of independent instances.
+ Synchronized_Suspend_Resume_Test ();
+ Synchronized_Suspend_Resume_Test (const Synchronized_Suspend_Resume_Test &);
+ Synchronized_Suspend_Resume_Test &
+ operator= (const Synchronized_Suspend_Resume_Test &);
+};
+
+Synchronized_Suspend_Resume_Test::Synchronized_Suspend_Resume_Test (
+ const ACE_UINT32 iterations)
+:
+ ACE_Task<ACE_MT_SYNCH> (),
+ iterations_ (iterations),
+ sem_ (0),
+ mutex_ (),
+ timer_ (),
+ high_ (sem_, mutex_, timer_),
+ mutex_acquire_release_time_ (0)
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Synchronized_Suspend_Resume_Test ctor\n"));
+#endif /* ACE_DEBUG_CST */
+
+ if (this->activate (THR_BOUND | THR_DETACHED | THR_SCHED_FIFO | new_lwp,
+ 1, 0, LOW_PRIORITY))
+ ACE_OS::perror (ACE_TEXT("activate"));
+}
+
+Synchronized_Suspend_Resume_Test::~Synchronized_Suspend_Resume_Test()
+{
+}
+
+int
+Synchronized_Suspend_Resume_Test::svc ()
+{
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Synchronized_Suspend_Resume_Test::svc (), entering"));
+
+ ACE_hthread_t thread_id;
+ ACE_Thread_Manager::instance ()->thr_self (thread_id);
+
+ ACE_DEBUG ((LM_DEBUG, "; thread ID is %u\n", thread_id));
+#endif /* ACE_DEBUG_CST */
+
+ {
+ Mutex_Acquire_Release_Test mutex_acquire_release_test (num_iterations);
+ mutex_acquire_release_test.svc ();
+ mutex_acquire_release_time_ =
+ static_cast<ACE_UINT32> (mutex_acquire_release_test.elapsed_time () /
+ num_iterations);
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "mutex_acquire_release: %u nsec\n",
+ mutex_acquire_release_time_));
+#endif /* ACE_DEBUG_CST */
+ }
+
+ high_.ready ();
+
+#if ACE_DEBUG_CST > 0
+ int priority, high_priority;
+ ACE_OS::thr_getprio (thread_id, priority);
+ ACE_OS::thr_getprio (high_.thread_id (), high_priority);
+ ACE_DEBUG ((LM_DEBUG,
+ "Synchronized_Suspend_Resume_Test::svc (), priority is %d, "
+ ", high thread priority is %d\n",
+ priority, high_priority));
+#endif /* ACE_DEBUG_CST */
+
+ // For information: the cost of the just the loop itself below,
+ // without the suspend and resume calls, on a 166 MHz Ultrasparc
+ // is about 12.3 nanoseconds per iteration.
+
+ ACE_UINT32 i;
+
+ for (i = 0; i < iterations_; ++i)
+ {
+#if ACE_DEBUG_CST > 0
+ if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0)
+ {
+ ACE_DEBUG ((LM_DEBUG,
+ "Synchronized_Suspend_Resume_Test::svc (), iteration "
+ "%d, continue high-priority thread %u\n",
+ i, high_.thread_id ()));
+ }
+#endif /* ACE_DEBUG_CST */
+
+ {
+ // Acquire the mutex so that the high priority thread will
+ // block after we signal it via the condition variable.
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, mutex_, -1);
+
+ // Release the semaphore so that the high priority thread can
+ // proceed.
+ if (sem_.release () != 0)
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "sem_.release"), -1);
+
+ timer_.start ();
+
+ // Release the mutex so that the high priority thread can
+ // proceed. The ACE_GUARD_RETURN macro implicity releases
+ // the mutex.
+ }
+ }
+
+ high_.done ();
+
+ // The high priority thread will be block on the semaphore, so
+ // release it.
+ if (sem_.release () != 0)
+ ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "sem_.release"), -1);
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG,
+ "Synchronized_Suspend_Resume_Test::svc: told high priority "
+ "task to terminate\n"));
+#endif /* ACE_DEBUG_CST */
+
+ // Resume the thread until thr_continue fails, indicating that it has
+ // finished.
+ for (i = 0; i < 10000 && ! ACE_OS::thr_continue (high_.thread_id ());
+ ++i) /* null */;
+
+#if ACE_DEBUG_CST > 0
+ ACE_DEBUG ((LM_DEBUG, "Synchronized_Suspend_Resume_Test::svc, finishing\n"));
+#endif /* ACE_DEBUG_CST */
+
+ return 0;
+}
+
+ACE_UINT32
+Synchronized_Suspend_Resume_Test::average_context_switch_time ()
+{
+ return high_.average_context_switch_time ();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// function get_options
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static
+u_int
+get_options (int argc, ACE_TCHAR *argv[])
+{
+ ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("c:n?"));
+ int opt;
+ while ((opt = get_opt ()) != EOF) {
+ switch (opt) {
+ case 'c':
+ if (ACE_OS::atoi (get_opt.opt_arg ()) >= 0)
+ {
+ counter = ACE_OS::atoi (get_opt.opt_arg ());
+ }
+ else
+ {
+ ACE_DEBUG ((LM_ERROR, "%n: count must be >= 0\n"));
+ return 1;
+ }
+ break;
+ case 'n':
+ new_lwp = THR_NEW_LWP;
+ break;
+ case '?':
+ ACE_DEBUG ((LM_ERROR, "usage: %n %s\n", usage));
+ ACE_OS::exit (1);
+ /* NOTREACHED */
+ break;
+ default:
+ ACE_DEBUG ((LM_ERROR, "%n: unknown arg, %c\n", opt));
+ ACE_DEBUG ((LM_ERROR, "usage: %n %s\n", usage));
+ return 1;
+ }
+ }
+
+ switch (argc - get_opt.opt_ind ()) {
+ case 0:
+ // use default number of iterations
+ break;
+ case 1:
+ if (ACE_OS::atoi (argv [get_opt.opt_ind ()]) > 0)
+ num_iterations = ACE_OS::atoi (argv [get_opt.opt_ind ()]);
+ else
+ {
+ ACE_DEBUG ((LM_ERROR, "%n: iterations must be > 0\n"));
+ return 1;
+ }
+ break;
+ default:
+ ACE_DEBUG ((LM_ERROR, "%n: too many arguments\n"));
+ ACE_DEBUG ((LM_ERROR, "usage: %n %s\n", usage));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// function main
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int
+ACE_TMAIN (int argc, ACE_TCHAR *argv [])
+{
+ ACE_LOG_MSG->open (argv[0] > 0 ? argv[0] : ACE_TEXT("context_switch_time"));
+
+ if (get_options (argc, argv))
+ ACE_OS::exit (-1);
+
+ // Disable LM_DEBUG.
+ ACE_Log_Msg::instance ()->priority_mask (ACE_LOG_MSG->priority_mask () ^
+ LM_DEBUG);
+
+#if defined (ACE_HAS_PENTIUM) && \
+ !defined (ACE_HAS_HI_RES_TIMER) && !defined (ACE_WIN32)
+ // Just to verify that ACE_High_Res_Timer::global_scale_factor ()
+ // correctly determines the clock speed.
+ ACE_DEBUG ((LM_INFO, "clock speed: %u MHz\n",
+ ACE_High_Res_Timer::global_scale_factor ()));
+#endif /* ACE_HAS_PENTIUM && ! ACE_HAS_HI_RES_TIMER && ! ACE_WIN32 */
+
+ if (ACE_OS::sched_params (
+ ACE_Sched_Params (
+ ACE_SCHED_FIFO,
+ ACE_Sched_Params::priority_min (ACE_SCHED_FIFO),
+ ACE_SCOPE_PROCESS)) != 0)
+ {
+ if (ACE_OS::last_error () == EPERM)
+ {
+ ACE_DEBUG ((LM_MAX, "context_switch_time: user is not superuser, "
+ "so remain in time-sharing class\n"));
+ }
+ else
+ {
+ ACE_OS::perror (ACE_TEXT("context_switch_time"));
+ ACE_OS::exit (-1);
+ }
+ }
+
+ HIGH_PRIORITY = ACE_Sched_Params::next_priority (ACE_SCHED_FIFO,
+ LOW_PRIORITY);
+ ACE_DEBUG ((LM_INFO, "low priority: %d, high priority: %d\n",
+ LOW_PRIORITY, HIGH_PRIORITY));
+
+ // Set the priority of this thread so that it's higher than any of
+ // the test threads. That might help avoid problems when waiting on
+ // those threads, below. It also seems to help the times
+ // significantly on LynxOS.
+ ACE_hthread_t self;
+ ACE_OS::thr_self (self);
+ ACE_OS::thr_setprio (ACE_Sched_Params::next_priority (ACE_SCHED_FIFO,
+ HIGH_PRIORITY));
+
+ bool forever = counter == 0;
+
+ ACE_Stats context_switch_test_stats;
+ ACE_Stats yield_test_stats;
+ ACE_Stats synchronized_suspend_resume_test_stats;
+
+ int suspend_resume_supported = 0;
+ // Check to see if thr_continue (), and therefore thr_suspend (),
+ // probably, are supported.
+ if (ACE_OS::thr_continue (self) == 0 || errno != ENOTSUP)
+ suspend_resume_supported = 1;
+
+ while (forever || counter-- > 0)
+ {
+ if (suspend_resume_supported)
+ {
+ // Run suspend/resume test first . . .
+ Suspend_Resume_Test suspend_resume_test (num_iterations);
+ // Wait for all tasks to exit.
+ ACE_Thread_Manager::instance ()->wait ();
+
+ // Then Ping Suspend/Resume test.
+ Ping_Suspend_Resume_Test ping_suspend_resume_test (num_iterations);
+ // Wait for all tasks to exit.
+ ACE_Thread_Manager::instance ()->wait ();
+
+ if (ping_suspend_resume_test.elapsed_time () >
+ suspend_resume_test.elapsed_time ())
+ {
+ context_switch_test_stats.
+ sample (ACE_U64_TO_U32 (
+ ping_suspend_resume_test.elapsed_time () -
+ suspend_resume_test.elapsed_time ()));
+
+ ACE_DEBUG ((LM_INFO, "context switch time is (%.3f - %.3f)/2 = "
+ "%.3f microseconds\n",
+ (double) ACE_UINT64_DBLCAST_ADAPTER (
+ ping_suspend_resume_test.elapsed_time ()) /
+ num_iterations,
+ (double) ACE_UINT64_DBLCAST_ADAPTER (
+ suspend_resume_test.elapsed_time ()) /
+ num_iterations,
+ (double) ACE_UINT64_DBLCAST_ADAPTER (
+ ping_suspend_resume_test.elapsed_time () -
+ suspend_resume_test.elapsed_time ()) /
+ num_iterations / 2u));
+ }
+ else
+ {
+ ACE_DEBUG ((LM_INFO, "ping suspend/resume time of %u usec was "
+ "less than suspend/resume time of %u\n",
+ ping_suspend_resume_test.elapsed_time () /
+ num_iterations,
+ suspend_resume_test.elapsed_time () /
+ num_iterations));
+ }
+ }
+
+ // Then Yield test.
+ Yield_Test yield_test (num_iterations);
+ // Wait for all tasks to exit.
+ ACE_Thread_Manager::instance ()->wait ();
+
+ yield_test_stats.sample (ACE_U64_TO_U32 (yield_test.elapsed_time ()));
+
+ // Try _really_ hard not to use floating point.
+ ACE_DEBUG ((LM_INFO, "context switch time from yield test is %u.%03u "
+ "microseconds\n",
+ (ACE_UINT32)
+ (yield_test.elapsed_time () / num_iterations / 2u),
+ (ACE_UINT32)
+ (yield_test.elapsed_time () % (num_iterations * 2u)) *
+ 1000u / num_iterations / 2u));
+
+ Synchronized_Suspend_Resume_Test
+ synchronized_suspend_resume_test (num_iterations);
+ // Wait for all tasks to exit.
+ ACE_Thread_Manager::instance ()->wait ();
+
+ synchronized_suspend_resume_test_stats.sample (
+ synchronized_suspend_resume_test.average_context_switch_time ());
+
+ ACE_DEBUG ((LM_INFO, "context switch time from synch susp/resume test "
+ "is %u.%03u microseconds\n",
+ synchronized_suspend_resume_test.
+ average_context_switch_time () / 1000u,
+ synchronized_suspend_resume_test.
+ average_context_switch_time () % 1000u));
+
+ // Give, e.g., Draft 4 Posix platforms a chance to cleanup threads.
+ const ACE_Time_Value half_sec (0L, 500000L);
+ ACE_OS::sleep (half_sec);
+ }
+
+ if (suspend_resume_supported)
+ {
+ ACE_OS::printf ("suspend-resume test: ");
+ context_switch_test_stats.print_summary (3, num_iterations * 2u);
+ }
+
+ ACE_OS::printf ("\nyield_test: ");
+ yield_test_stats.print_summary (3, num_iterations * 2u);
+
+ ACE_OS::printf ("\nsynchronized suspend-resume test: ");
+ synchronized_suspend_resume_test_stats.print_summary (3,
+ 1000u /* nsec/usec */);
+
+ return 0;
+}
+#else
+int
+main (int, char *[])
+{
+ ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n"));
+ return 0;
+}
+#endif /* ACE_HAS_THREADS */
+
+
+// EOF