diff options
author | levine <levine@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1997-01-29 15:27:25 +0000 |
---|---|---|
committer | levine <levine@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1997-01-29 15:27:25 +0000 |
commit | 792f1966521cb91e6693f0625fad20a009f4d3ec (patch) | |
tree | 5fcf76e306348245ea7bf9bcd4aaf5250e338b11 /performance-tests/Misc | |
parent | f6cdabeb273f69e4448695b290c919f49c3acd40 (diff) | |
download | ATCD-792f1966521cb91e6693f0625fad20a009f4d3ec.tar.gz |
added context_switch_time.cpp
Diffstat (limited to 'performance-tests/Misc')
-rw-r--r-- | performance-tests/Misc/context_switch_time.cpp | 726 |
1 files changed, 726 insertions, 0 deletions
diff --git a/performance-tests/Misc/context_switch_time.cpp b/performance-tests/Misc/context_switch_time.cpp new file mode 100644 index 00000000000..15ac5f97574 --- /dev/null +++ b/performance-tests/Misc/context_switch_time.cpp @@ -0,0 +1,726 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// (none) +// +// = FILENAME +// context_switch_time.cpp +// +// = DESCRIPTION +// Program that calculates context switch time between threads. +// This 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) +// Ping Suspend/Resume Task and Suspend/Resume Task benchmarks. +// Laboratory benchmark +// It measures two different times: +// 1) the time to resume a block 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. +// +// On Solaris 2.5.1, real-time threads must be bound to LWPs (using the +// THR_BOUND flag), so that they can compete for system-wide resources. +// In other words, if a thread is bound to an LWP, then the kernel is +// aware of it. +// +// On Solaris 2.5.1, a call to thr_yield () is necessary after a call +// to thr_continue () by a low-priority task. Without it, the high-priority +// task doesn't preempt the low-priority task. This happens even with a +// 10 nsec time quantum for the LWP. Maybe it's because with this version +// of Solaris, the scheduling policy is SCHED_OTHER. +// +// All threads are created with the THR_DETACHED flag so that their +// resources are released when they terminate. +// +// = CREATION DATE +// 17 January 1997 +// +// = AUTHOR +// David L. Levine +// +// ============================================================================ + +static const char usage [] = "[-? |\n" + " [-c <repeat count, 0 means forever>]\n" + " [-n to spawn a new LWP with each thread\n" + "[<iterations>]]"; + +#include "Timer.h" +#include "ace/Scheduling_Params.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/Synch.h" +#include "ace/Get_Opt.h" +#include <iomanip.h> + +#if !defined (DEBUG) +#define DEBUG 0 +#endif /* DEBUG */ + +#if defined (__sun) +/* Solaris priority values range from low to high with increasing priority */ +static const unsigned int LOW_PRIORITY = 1; +static const unsigned int HIGH_PRIORITY = 2; +#else +/* VxWorks and Win32 priority values range from high to low with + increasing priority */ +static const unsigned int LOW_PRIORITY = 2; +static const unsigned int HIGH_PRIORITY = 1; +#endif + + +// global test configuration parameters +static unsigned long count = 1; +static unsigned long iterations = 1000; +static unsigned int 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 close (); + + 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_Service_Config::thr_mgr ()), + initialized_ (0), // initialize to locked, then unlock when ready + blocked_semaphore_ (0) +{ +#if DEBUG > 0 + cout << "Low_Priority_Null_Task ctor" << endl; +#endif /* DEBUG */ + + this->activate (THR_BOUND | THR_DETACHED | new_lwp, 1, 0, LOW_PRIORITY); + +#if DEBUG > 0 + cout << "Low_Priority_Null_Task ctor, activated" << endl; +#endif /* DEBUG */ +} + +Low_Priority_Null_Task::~Low_Priority_Null_Task() +{ +} + +int +Low_Priority_Null_Task::svc () +{ +#if DEBUG > 0 + cout << "Low_Priority_Null_Task::svc (), entering" << ::flush; +#endif /* DEBUG */ + + ACE_Service_Config::thr_mgr ()->thr_self (thread_id_); + initialized_.release (); + +#if DEBUG > 0 + cout << "; thread ID is " << thread_id_ << endl; +#endif /* DEBUG */ + + // this task must never actually execute, so just have it block + // on a semaphore forever . . . + blocked_semaphore_.acquire (); + +#if DEBUG > 0 + cout << "Low_Priority_Task::svc, finishing" << endl; +#endif /* DEBUG */ + + return 0; +} + +void +Low_Priority_Null_Task::close () +{ + blocked_semaphore_.release (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Suspend_Resume_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class Suspend_Resume_Test : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Suspend_Resume_Test (const unsigned long iterations); + virtual ~Suspend_Resume_Test (); + + virtual int svc (); + + unsigned long elapsed_time () const { return elapsed_time_; } +private: + const unsigned long iterations_; + + Low_Priority_Null_Task low_; + + Timer timer_; + + unsigned long 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 unsigned long iterations) : + ACE_Task<ACE_MT_SYNCH> (), + iterations_ (iterations), + low_ (), + timer_ () +{ +#if DEBUG > 0 + cout << "Suspend_Resume_Test ctor" << endl; +#endif /* DEBUG */ + + this->activate (THR_BOUND | THR_DETACHED | new_lwp, 1, 0, HIGH_PRIORITY); +} + +Suspend_Resume_Test::~Suspend_Resume_Test() +{ +} + +int +Suspend_Resume_Test::svc () +{ +#if DEBUG > 0 + ACE_hthread_t thread_id; + ACE_Service_Config::thr_mgr ()->thr_self (thread_id); + + cout << "Suspend_Resume_Test::svc (), thread ID is " << thread_id << endl; +#endif /* DEBUG */ + + 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 (unsigned long i = 0; i < iterations_; ++i) + { +#if DEBUG > 0 + if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0) + { + cout << "Suspend_Resume_Test::svc (), iteration " << i << endl; + } +#endif /* DEBUG */ + + ACE_OS::thr_suspend (low_.thread_id ()); + ACE_OS::thr_continue (low_.thread_id ()); + ACE_OS::thr_yield (); + } + + timer_.stop (); + elapsed_time_ = timer_.get_time (); + + low_.close (); + +#if DEBUG > 0 + cout << "Suspend_Resume_Test::svc, finishing" << endl; +#endif /* DEBUG */ + + 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 close (); + + ACE_hthread_t thread_id () const { return thread_id_; } + unsigned long iterations () const { return iterations_; } +private: + ACE_hthread_t thread_id_; + ACE_Semaphore initialized_; // block until thread_id_ is assigned + int terminate_; + unsigned long 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_Service_Config::thr_mgr ()), + initialized_ (0), // initialize to locked, then unlock when ready + terminate_ (0), + iterations_ (0) +{ +#if DEBUG > 0 + cout << "High_Priority_Simple_Task ctor" << endl; +#endif /* DEBUG */ + + this->activate (THR_BOUND | THR_DETACHED | new_lwp, 1, 0, HIGH_PRIORITY); + +#if DEBUG > 0 + cout << "High_Priority_Simple_Task ctor, activated" << endl; +#endif /* DEBUG */ +} + +High_Priority_Simple_Task::~High_Priority_Simple_Task() +{ +} + +int +High_Priority_Simple_Task::svc () +{ +#if DEBUG > 0 + cout << "High_Priority_Simple_Task::svc (), entering" << ::flush; +#endif /* DEBUG */ + + ACE_Service_Config::thr_mgr ()->thr_self (thread_id_); + initialized_.release (); + +#if DEBUG > 0 + cout << "; thread ID is " << thread_id_ << endl; +#endif /* DEBUG */ + + while (! terminate_) + { +#if DEBUG > 0 + cout << "High_Priority_Simple_Task::svc, suspend self (" + << thread_id_ << ")" << endl; +#endif /* DEBUG */ + + ++iterations_; + + // immediately suspend self + ACE_OS::thr_suspend (thread_id_); + +#if DEBUG > 0 + cout << "High_Priority_Simple_Task::svc, resumed (" + << thread_id_ << ")" << endl; +#endif /* DEBUG */ + } + +#if DEBUG > 0 + cout << "High_Priority_Simple_Task::svc, finishing" << endl; +#endif /* DEBUG */ + + return 0; +} + +inline +void +High_Priority_Simple_Task::close () +{ + terminate_ = 1; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Ping_Suspend_Resume_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class Ping_Suspend_Resume_Test : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Ping_Suspend_Resume_Test (const unsigned long iterations); + virtual ~Ping_Suspend_Resume_Test (); + + virtual int svc (); + + unsigned long elapsed_time () const { return elapsed_time_; } +private: + const unsigned long iterations_; + + High_Priority_Simple_Task high_; + + Timer timer_; + + unsigned long 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 unsigned long + iterations) : + ACE_Task<ACE_MT_SYNCH> (), + iterations_ (iterations), + high_ (), + timer_ () +{ +#if DEBUG > 0 + cout << "Ping_Suspend_Resume_Test ctor" << endl; +#endif /* DEBUG */ + + this->activate (THR_BOUND | THR_DETACHED | new_lwp, 1, 0, LOW_PRIORITY); +} + +Ping_Suspend_Resume_Test::~Ping_Suspend_Resume_Test() +{ +} + +int +Ping_Suspend_Resume_Test::svc () +{ +#if DEBUG > 0 + cout << "Ping_Suspend_Resume_Test::svc (), entering" << ::flush; + + ACE_hthread_t thread_id; + ACE_Service_Config::thr_mgr ()->thr_self (thread_id); + + cout << "; thread ID is " << thread_id << endl; +#endif /* DEBUG */ + + high_.ready (); + +#if DEBUG > 0 + int priority, high_priority; + ACE_OS::thr_getprio (thread_id, priority); + ACE_OS::thr_getprio (high_.thread_id (), high_priority); + cout << "Ping_Suspend_Resume_Test::svc (), priority is " + << priority << ", high thread priority is " + << high_priority << endl; +#endif /* DEBUG */ + + // 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 (unsigned long i = 0; i < iterations_; ++i) + { +#if DEBUG > 0 + if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0) + { + cout << "Ping_Suspend_Resume_Test::svc (), iteration " << i + << ", continue high-priority thread " << high_.thread_id () + << endl; + } +#endif /* DEBUG */ + ACE_OS::thr_continue (high_.thread_id ()); + ACE_OS::thr_yield (); + } + + timer_.stop (); + elapsed_time_ = timer_.get_time (); + + high_.close (); +#if DEBUG > 0 + cout << "Ping_Suspend_Resume_Test::svc: told high priority task to terminate" + << endl; +#endif /* DEBUG */ + + // resume the thread just one more time, to let it finish execution . . . + ACE_OS::thr_continue (high_.thread_id ()); + ACE_OS::thr_yield (); + + // don't count the one iteration that was used to allow the high-priority + // thread to terminate + if (high_.iterations () < iterations_) + { + cout << "high priority task executed only " << high_.iterations () + << " iterations!" << endl; + } + +#if DEBUG > 0 + cout << "Ping_Suspend_Resume_Test::svc, finishing" << endl; +#endif /* DEBUG */ + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Yield_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class Yield_Test : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Yield_Test (const unsigned long iterations); + virtual ~Yield_Test (); + + virtual int svc (); + + unsigned long elapsed_time () const { return elapsed_time_; } +private: + const unsigned long iterations_; + + Timer timer_; + + unsigned long 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 unsigned long iterations) : + ACE_Task<ACE_MT_SYNCH> (), + iterations_ (iterations), + timer_ () +{ +#if DEBUG > 0 + cout << "Yield_Test ctor" << endl; +#endif /* DEBUG */ + + this->activate (THR_BOUND | THR_DETACHED | new_lwp, 2, 0, LOW_PRIORITY); +} + +Yield_Test::~Yield_Test() +{ +} + +int +Yield_Test::svc () +{ +#if DEBUG > 0 + cout << "Yield_Test::svc (), entering" << ::flush; + + ACE_hthread_t thread_id; + ACE_Service_Config::thr_mgr ()->thr_self (thread_id); + + int priority; + ACE_OS::thr_getprio (thread_id, priority); + + cout << "; thread ID is " << thread_id + << ", priority is " << priority << endl; +#endif /* DEBUG */ + + timer_.start (); + + for (unsigned long i = 0; i < iterations_; ++i) + { +#if DEBUG > 0 + if (i % (iterations_ >= 10 ? iterations_ / 10 : 1) == 0) + { + cout << "Yield_Test::svc () [" << thread_id << "], iteration " << i + << endl; + } +#endif /* DEBUG */ + ACE_OS::thr_yield (); + } + + timer_.stop (); + elapsed_time_ = timer_.get_time (); + +#if DEBUG > 0 + cout << "Yield_Test::svc, finishing" << endl; +#endif /* DEBUG */ + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function get_options +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static +unsigned int +get_options (int argc, char *argv []) +{ + ACE_Get_Opt get_opt (argc, argv, "c:n?"); + int opt; + while ((opt = get_opt ()) != EOF) { + switch (opt) { + case 'c': + if (ACE_OS::atoi (get_opt.optarg) >= 0) + { + count = ACE_OS::atoi (get_opt.optarg); + } + else + { + cerr << argv [0] << ": count must be >= 0" << endl; + return 1; + } + break; + case 'n': + new_lwp = THR_NEW_LWP; + break; + case '?': + cout << "usage: " << argv [0] << " " << usage << endl; + ACE_OS::exit (0); + break; + default: + cerr << argv [0] << ": unknown arg, " << (char) opt << endl; + cerr << "usage: " << argv [0] << " " << usage << endl; + return 1; + } + } + + switch (argc - get_opt.optind) { + case 0: + // use default number of iterations + break; + case 1: + if (ACE_OS::atoi (argv [get_opt.optind]) > 0) + { + iterations = ACE_OS::atoi (argv [get_opt.optind]); + } + else + { + cerr << argv [0] << ": iterations must be > 0" << endl; + return 1; + } + break; + default: + cerr << argv [0] << ": too many arguments" << endl; + cerr << "usage: " << argv [0] << " " << usage << endl; + return 1; + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function main +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +int +main (int argc, char *argv []) +{ + if (get_options (argc, argv)) ACE_OS::exit (-1); + + if (ACE_OS::set_sched_params ( + ACE_Scheduling_Params ( + ACE_Thread_Priority(ACE_Thread_Priority::ACE_REALTIME_PRIORITY_CLASS, + ACE_Thread_Priority::ACE_PRIORITY_4))) != 0) + { + if (ACE_OS::last_error () == EPERM) + { + ACE_OS::fprintf (stderr, "%s: user is not root, so remain in " + "time-sharing class\n", argv[0]); + } + else + { + ACE_OS::perror (argv[0]); + ACE_OS::exit (-1); + } + } + + int forever = count == 0; + + while (forever || count-- > 0) + { + // run suspend/resume test first . . . + Suspend_Resume_Test suspend_resume_test (iterations); + // Wait for all tasks to exit. + ACE_Service_Config::thr_mgr ()->wait (); + + // then Ping Suspend/Resume test + Ping_Suspend_Resume_Test ping_suspend_resume_test (iterations); + // Wait for all tasks to exit. + ACE_Service_Config::thr_mgr ()->wait (); + + if (ping_suspend_resume_test.elapsed_time () > + suspend_resume_test.elapsed_time ()) + { + cout << "context switch time is (" + << setw (9) + << (double) ping_suspend_resume_test.elapsed_time () / + iterations + << " - " + << setw (9) + << (double) suspend_resume_test.elapsed_time () / iterations + << ")/2 = " + << setw (9) + << (double) (ping_suspend_resume_test.elapsed_time () - + suspend_resume_test.elapsed_time ()) / iterations / + 2 + << " microseconds" << endl; + } + else + { + cout << "ping suspend/resume time of " + << (double) ping_suspend_resume_test.elapsed_time () / + iterations + << " usec was less than suspend/resume time of " + << (double) suspend_resume_test.elapsed_time () / iterations + << endl; + } + + // then Yield test + Yield_Test yield_test (iterations); + // Wait for all tasks to exit. + ACE_Service_Config::thr_mgr ()->wait (); + + cout << "context switch time from yield test is " + << (double) yield_test.elapsed_time () / iterations / 2 + << " microseconds" + << endl; + } + + return 0; +} + + +// EOF |