diff options
Diffstat (limited to 'ACE/performance-tests/Misc')
-rw-r--r-- | ACE/performance-tests/Misc/Makefile.am | 157 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/Misc.mpc | 67 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/basic_func.cpp | 49 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/basic_func.h | 77 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/basic_perf.cpp | 646 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/childbirth_time.cpp | 403 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/context_switch_time.cpp | 1318 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/preempt.cpp | 467 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/test_guard.cpp | 119 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/test_mutex.cpp | 238 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/test_naming.cpp | 199 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/test_singleton.cpp | 179 | ||||
-rw-r--r-- | ACE/performance-tests/Misc/test_singleton.h | 24 |
13 files changed, 3943 insertions, 0 deletions
diff --git a/ACE/performance-tests/Misc/Makefile.am b/ACE/performance-tests/Misc/Makefile.am new file mode 100644 index 00000000000..e454644170b --- /dev/null +++ b/ACE/performance-tests/Misc/Makefile.am @@ -0,0 +1,157 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## ./bin/mwc.pl -type automake -noreldefs ACE.mwc + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.Misc_Basic_Perf.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += basic_perf + +basic_perf_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +basic_perf_SOURCES = \ + basic_func.cpp \ + basic_perf.cpp \ + basic_func.h + +basic_perf_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Misc_Childbirth_Time.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += childbirth_time + +childbirth_time_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +childbirth_time_SOURCES = \ + childbirth_time.cpp \ + basic_func.h \ + test_singleton.h + +childbirth_time_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Misc_Context_Switch_Time.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += context_switch_time + +context_switch_time_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +context_switch_time_SOURCES = \ + context_switch_time.cpp \ + basic_func.h \ + test_singleton.h + +context_switch_time_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Misc_Preempt.am + +noinst_PROGRAMS += preempt + +preempt_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +preempt_SOURCES = \ + preempt.cpp \ + basic_func.h \ + test_singleton.h + +preempt_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Misc_Test_Mutex.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += test_mutex + +test_mutex_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_mutex_SOURCES = \ + test_mutex.cpp \ + basic_func.h \ + test_singleton.h + +test_mutex_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Misc_Test_Naming.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += test_naming + +test_naming_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_naming_SOURCES = \ + test_naming.cpp \ + basic_func.h \ + test_singleton.h + +test_naming_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Misc_Test_Singleton.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += test_singleton + +test_singleton_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_singleton_SOURCES = \ + test_singleton.cpp \ + test_singleton.h + +test_singleton_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/performance-tests/Misc/Misc.mpc b/ACE/performance-tests/Misc/Misc.mpc new file mode 100644 index 00000000000..3be2b7e3078 --- /dev/null +++ b/ACE/performance-tests/Misc/Misc.mpc @@ -0,0 +1,67 @@ +// -*- MPC -*- +// $Id$ + +project(*basic_perf) : aceexe { + avoids += ace_for_tao + exename = basic_perf + Source_Files { + basic_func.cpp + basic_perf.cpp + } +} + +project(*childbirth_time) : aceexe { + avoids += ace_for_tao + exename = childbirth_time + Source_Files { + childbirth_time.cpp + } +} + +project(*context_switch_time) : aceexe { + avoids += ace_for_tao + exename = context_switch_time + Source_Files { + context_switch_time.cpp + } +} + +project(*test_mutex) : aceexe { + avoids += ace_for_tao + exename = test_mutex + Source_Files { + test_mutex.cpp + } +} + +project(*test_naming) : aceexe { + avoids += ace_for_tao + requires += ace_other + exename = test_naming + Source_Files { + test_naming.cpp + } +} + +project(*test_singleton) : aceexe { + avoids += ace_for_tao + exename = test_singleton + Source_Files { + test_singleton.cpp + } +} + +project(*preempt) : aceexe { + exename = preempt + Source_Files { + preempt.cpp + } +} + +// May need to add an ace_obsolete_guard_class feature +//project(*test_guard) : aceexe { +// exename = test_guard +// Source_Files { +// test_guard.cpp +// } +//} diff --git a/ACE/performance-tests/Misc/basic_func.cpp b/ACE/performance-tests/Misc/basic_func.cpp new file mode 100644 index 00000000000..a76565d74fb --- /dev/null +++ b/ACE/performance-tests/Misc/basic_func.cpp @@ -0,0 +1,49 @@ +// $Id$ + +#include "basic_func.h" + +ACE_RCSID(Misc, basic_func, "$Id$") + +int A, BB, C, D, E, F; + +void +func () +{ + DO_SOMETHING +} + + +void +Foo::func () +{ + DO_SOMETHING +} + + +Foo_v::~Foo_v () +{ +} + + +void +Foo_v::func () +{ + DO_SOMETHING +} + + +void +Foo_v::v_func () +{ + DO_SOMETHING +} + + +void +Foo_d_v::v_func () +{ + DO_SOMETHING +} + + +// EOF diff --git a/ACE/performance-tests/Misc/basic_func.h b/ACE/performance-tests/Misc/basic_func.h new file mode 100644 index 00000000000..bbaf4d4cfb9 --- /dev/null +++ b/ACE/performance-tests/Misc/basic_func.h @@ -0,0 +1,77 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance-tests/Misc +// +// = FILENAME +// basic_func.h +// +// = DESCRIPTION +// For use with basic_perf.cpp. +// +// = AUTHOR +// David Levine +// +// ============================================================================ + +#ifndef BASIC_FUNC_H +#define BASIC_FUNC_H + +#include "ace/ACE.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +extern int A,BB,C,D,E,F; + +// If your compiler optimizes away Empty_Iteration_Test::run (), then +// #defining ACE_HAS_OPTIMIZED_NULL_FUNCTIONS may help produce more +// reasonable numbers. +#if defined (_MSC_VER) + // MSVC 5.0 needs it . . . +# define ACE_HAS_OPTIMIZED_NULL_FUNCTIONS +#endif /* _MSC_VER */ + +#if defined (ACE_HAS_OPTIMIZED_NULL_FUNCTIONS) +# define EXPR(R,A,BB,C,D) (R=(A*BB + C*D)) +# define DO_SOMETHING EXPR(A,BB,EXPR(F,A,E,C,BB),EXPR(BB,F,A,D,E),EXPR(E,BB,F,A,C)); +#else /* ACE_HAS_OPTIMIZED_NULL_FUNCTIONS */ +# define DO_SOMETHING +#endif /* ACE_HAS_OPTIMIZED_NULL_FUNCTIONS */ + + +// An external (global) function. +void func (); + + +// A class with no virtual functions. +class Foo +{ + public: + void inline_func () { DO_SOMETHING } + void func (); +}; + + +// A class with a virtual function. +class Foo_v +{ + public: + virtual ~Foo_v (); + void inline_func () { DO_SOMETHING } + void func (); + virtual void v_func (); +}; + + +// A derived class. +class Foo_d_v : public Foo_v +{ + public: + virtual void v_func (); +}; + +#endif /* BASIC_FUNC_H */ diff --git a/ACE/performance-tests/Misc/basic_perf.cpp b/ACE/performance-tests/Misc/basic_perf.cpp new file mode 100644 index 00000000000..d084e84e1b2 --- /dev/null +++ b/ACE/performance-tests/Misc/basic_perf.cpp @@ -0,0 +1,646 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance-tests/Misc +// +// = FILENAME +// basic_perf.cpp +// +// = DESCRIPTION +// Times various simple operations. +// +// With Sun C++, use -O2: make CFLAGS="-mt -O2" BIN=basic_perf +// -fast seems to produce slower times. +// +// = AUTHOR +// David Levine +// +// ============================================================================ + +#include "basic_func.h" +#include "ace/High_Res_Timer.h" +#include "ace/Get_Opt.h" +#include "ace/OS_main.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_sys_utsname.h" + +ACE_RCSID(Misc, basic_perf, "$Id$") + +static const char usage [] = "[-? |\n" + " [-i <iterations> [1000000]]"; + +// These are global. They appear to bust the CPU cache, on an Ultra 2 +// w/Sun C++ 4.2. +static u_int iterations = 1000000; +Foo foo; +Foo_v foo_v; + +inline +void +inline_func () +{ + DO_SOMETHING +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function per_iteration +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Given an elapsed time in nanoseconds, returns the time per iteration +// in microseconds. +static +inline +double +per_iteration (const ACE_hrtime_t elapsed /* nanoseconds */) +{ + double ms_per_iteration = (double) ACE_CU64_TO_CU32 (elapsed) / 1000.0 / + (double) iterations; + + // Don't print out "-0.000" or "-0.001" . . . + return -0.002 < ms_per_iteration && ms_per_iteration < 0.0 + ? 0.0 + : ms_per_iteration; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Basic_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Basic_Test +{ +public: + Basic_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time); + virtual ~Basic_Test (void); + + virtual void run (void) = 0; + + double iteration_time (void); + + void print_iteration_time (const char *message); + +protected: + ACE_hrtime_t elapsed_time_; + + void start_timing (void) + { + timer_.reset (); + timer_.start (); + } + + void stop_timing (void) + { + timer_.stop (); + timer_.elapsed_time (elapsed_time_); + } + +private: + ACE_High_Res_Timer &timer_; + ACE_hrtime_t empty_iteration_time_; + + // Require the timer reference. + Basic_Test (void); + + // Force construction of independent instances by prohibiting copying. + Basic_Test (const Basic_Test &); + Basic_Test &operator= (const Basic_Test &); +}; + +Basic_Test::Basic_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : elapsed_time_ (0), + timer_ (timer), + empty_iteration_time_ (empty_iteration_time) +{ +} + +Basic_Test::~Basic_Test (void) +{ +} + +double +Basic_Test::iteration_time (void) +{ + return per_iteration (elapsed_time_ > empty_iteration_time_ ? + elapsed_time_ - empty_iteration_time_ : + static_cast<ACE_hrtime_t> (0)); +} + +void +Basic_Test::print_iteration_time (const char *message) +{ + ACE_DEBUG ((LM_INFO, " %-41s %8.3f\n", + message, + this->iteration_time ())); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Empty_Iteration_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Empty_Iteration_Test : public Basic_Test +{ +public: + Empty_Iteration_Test (ACE_High_Res_Timer &timer) : Basic_Test (timer, 0) {} + virtual ~Empty_Iteration_Test (void) {}; + + virtual void run (void); + + ACE_hrtime_t empty_iteration_time (void) const + { + return elapsed_time_; + } + +private: + // Require the timer reference. + Empty_Iteration_Test (void); + + // Force construction of independent instances by prohibiting copying. + Empty_Iteration_Test (const Empty_Iteration_Test &); + Empty_Iteration_Test &operator= (const Empty_Iteration_Test &); +}; + +void +Empty_Iteration_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + { + DO_SOMETHING + } + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Inline_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Inline_Call_Test : public Basic_Test +{ +public: + Inline_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Inline_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Inline_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Inline_Call_Test (const Inline_Call_Test &); + Inline_Call_Test &operator= (const Inline_Call_Test &); +}; + +void +Inline_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + inline_func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Noninline_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Noninline_Call_Test : public Basic_Test +{ +public: + Noninline_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Noninline_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Noninline_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Noninline_Call_Test (const Noninline_Call_Test &); + Noninline_Call_Test &operator= (const Noninline_Call_Test &); +}; + +void +Noninline_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Inline_Member_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Inline_Member_Call_Test : public Basic_Test +{ +public: + Inline_Member_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Inline_Member_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Inline_Member_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Inline_Member_Call_Test (const Inline_Member_Call_Test &); + Inline_Member_Call_Test &operator= (const Inline_Member_Call_Test &); +}; + +void +Inline_Member_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + foo.inline_func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Noninline_Member_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Noninline_Member_Call_Test : public Basic_Test +{ +public: + Noninline_Member_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Noninline_Member_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Noninline_Member_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Noninline_Member_Call_Test (const Noninline_Member_Call_Test &); + Noninline_Member_Call_Test &operator= (const Noninline_Member_Call_Test &); +}; + +void +Noninline_Member_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + foo.func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Inline_Member_With_Virtual_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Inline_Member_With_Virtual_Call_Test : public Basic_Test +{ +public: + Inline_Member_With_Virtual_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Inline_Member_With_Virtual_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Inline_Member_With_Virtual_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Inline_Member_With_Virtual_Call_Test ( + const Inline_Member_With_Virtual_Call_Test &); + Inline_Member_With_Virtual_Call_Test &operator= ( + const Inline_Member_With_Virtual_Call_Test &); +}; + +void +Inline_Member_With_Virtual_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + foo_v.inline_func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Noninline_Member_With_Virtual_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Noninline_Member_With_Virtual_Call_Test : public Basic_Test +{ +public: + Noninline_Member_With_Virtual_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Noninline_Member_With_Virtual_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Noninline_Member_With_Virtual_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Noninline_Member_With_Virtual_Call_Test + (const Noninline_Member_With_Virtual_Call_Test &); + Noninline_Member_With_Virtual_Call_Test &operator= + (const Noninline_Member_With_Virtual_Call_Test &); +}; + +void +Noninline_Member_With_Virtual_Call_Test::run (void) +{ + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + foo_v.func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Virtual_Member_Optimizable_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Virtual_Member_Optimizable_Call_Test : public Basic_Test +{ +public: + Virtual_Member_Optimizable_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Virtual_Member_Optimizable_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Virtual_Member_Optimizable_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Virtual_Member_Optimizable_Call_Test ( + const Virtual_Member_Optimizable_Call_Test &); + Virtual_Member_Optimizable_Call_Test &operator= ( + const Virtual_Member_Optimizable_Call_Test &); +}; + +void +Virtual_Member_Optimizable_Call_Test::run (void) +{ + Foo_v &fv_o = foo_v; + + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + fv_o.v_func (); + + this->stop_timing (); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Virtual_Member_Call_Test +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class Virtual_Member_Call_Test : public Basic_Test +{ +public: + Virtual_Member_Call_Test (ACE_High_Res_Timer &timer, + ACE_hrtime_t empty_iteration_time) + : Basic_Test (timer, empty_iteration_time) {} + virtual ~Virtual_Member_Call_Test (void) {}; + + virtual void run (void); + +private: + // Require the timer reference. + Virtual_Member_Call_Test (void); + + // Force construction of independent instances by prohibiting copying. + Virtual_Member_Call_Test (const Virtual_Member_Call_Test &); + Virtual_Member_Call_Test &operator= (const Virtual_Member_Call_Test &); +}; + +void +Virtual_Member_Call_Test::run (void) +{ + Foo_v *fv; + + if (iterations < 2) + fv = new Foo_v; + else + fv = new Foo_d_v; + + this->start_timing (); + + for (u_int i = 0; i < iterations; ++i) + fv->v_func (); + + this->stop_timing (); + + delete fv; + fv = 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function get_options +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static +unsigned int +get_options (int argc, ACE_TCHAR *argv []) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("i:?")); + int opt; + + while ((opt = get_opt ()) != EOF) + { + switch (opt) + { + case 'i': + { + int temp = ACE_OS::atoi (get_opt.opt_arg ()); + if (temp > 0) + iterations = (u_int) temp; + else + { + ACE_DEBUG ((LM_ERROR, "%s: number of iterations must be > 0\n", + argv [0])); + return 1; + } + break; + } + case '?': + ACE_DEBUG ((LM_INFO, "usage: %s %s\n", argv [0], usage)); + ACE_OS::exit (0); + break; + default: + ACE_DEBUG ((LM_ERROR, "%s: unknown arg, %c\n", argv [0], + (char) opt)); + ACE_DEBUG ((LM_ERROR, "usage: %s %s\n", argv [0], usage)); + return 1; + } + } + + switch (argc - get_opt.opt_ind ()) + { + case 0: + // OK + break; + default: + ACE_DEBUG ((LM_ERROR, "%s: too many arguments\n", argv [0])); + ACE_DEBUG ((LM_ERROR, "usage: %s %s\n", argv [0], usage)); + return 1; + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function main +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (get_options (argc, argv)) + ACE_OS::exit (-1); + + ACE_utsname name; + + if (ACE_OS::uname (&name) != -1) + ACE_DEBUG ((LM_INFO, "%s (%s), %s %s at %T\n", + name.nodename, name.machine, name.sysname, name.release)); + + ACE_DEBUG ((LM_INFO, "%u iterations\n", iterations)); + + + // Use one timer for all the tests. No particular reason why. + ACE_High_Res_Timer timer; + + // Calculate the time for an "empty iteration", and subtract it + // from each test time. + ACE_hrtime_t iteration_time; + + Empty_Iteration_Test empty_iteration_test (timer); + empty_iteration_test.run (); + ACE_DEBUG ((LM_INFO, "An empty iteration costs %8.3f microseconds.\n\n", + empty_iteration_test.iteration_time ())); + iteration_time = empty_iteration_test.empty_iteration_time (); + + + // Run the real tests . . . + Inline_Call_Test inline_call_test (timer, iteration_time); + inline_call_test.run (); + + Noninline_Call_Test noninline_call_test (timer, iteration_time); + noninline_call_test.run (); + + Inline_Member_Call_Test inline_member_call_test (timer, iteration_time); + inline_member_call_test.run (); + + Noninline_Member_Call_Test noninline_member_call_test ( + timer, iteration_time); + noninline_member_call_test.run (); + + Inline_Member_With_Virtual_Call_Test + inline_member_with_virtual_call_test (timer, iteration_time); + inline_member_with_virtual_call_test.run (); + + Noninline_Member_With_Virtual_Call_Test + noninline_member_with_virtual_call_test (timer, iteration_time); + noninline_member_with_virtual_call_test.run (); + + Virtual_Member_Optimizable_Call_Test + virtual_member_optimizable_call_test (timer, iteration_time); + virtual_member_optimizable_call_test.run (); + + Virtual_Member_Call_Test virtual_member_call_test (timer, iteration_time); + virtual_member_call_test.run (); + + + // Print results . . . + ACE_DEBUG ((LM_INFO, "operation " + "time, microseconds\n")); + ACE_DEBUG ((LM_INFO, "========= " + "==================")); + + ACE_DEBUG ((LM_INFO, "\nglobal function calls:\n")); + inline_call_test.print_iteration_time ("inline function call"); + noninline_call_test.print_iteration_time ("non-inline function call"); + + ACE_DEBUG ((LM_INFO, "\nmember function calls:\n")); + inline_member_call_test.print_iteration_time ( + "inline member function call"); + noninline_member_call_test.print_iteration_time ( + "non-inline member function call"); + + ACE_DEBUG ((LM_INFO, "\nmember function calls, class has a virtual " + "function:\n")); + inline_member_with_virtual_call_test.print_iteration_time ( + "inline member function with virtual call"); + noninline_member_with_virtual_call_test.print_iteration_time ( + "non-inline member function w/virtual call"); + + ACE_DEBUG ((LM_INFO, "\nvirtual member function calls:\n")); + virtual_member_optimizable_call_test.print_iteration_time ( + "virtual member function call, optimizable"); + virtual_member_call_test.print_iteration_time ( + "virtual member function call"); + + return 0; +} + + +// EOF diff --git a/ACE/performance-tests/Misc/childbirth_time.cpp b/ACE/performance-tests/Misc/childbirth_time.cpp new file mode 100644 index 00000000000..913b4de0e25 --- /dev/null +++ b/ACE/performance-tests/Misc/childbirth_time.cpp @@ -0,0 +1,403 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// (none) +// +// = FILENAME +// childbirth_time.cpp +// +// = DESCRIPTION +// This program is used to measure various child-creation mechanisms +// on various platforms. By default, the program measure the time +// to 'fork' a new process using ACE_Process.spawn (). Other tests +// are possible as described below. James Hu provides the idea to +// batch measuring threads creation. +// +// Usage: childbirth_time [-n ###] [-l ###] [-p|-f|-t|-a|-m|-x] [-h] [-e] +// +// -n ###: Specify number of iteration in tens. If this +// option is not specified, the default is +// MULTIPLY_FACTOR * (100 iterations,) which is +// equivalent to -n 10. +// +// -l ###: Specify MULTIPLY_FACTOR. Default is 10. +// +// *-p: Measure the performance of forking a child process +// and exec an "empty" program. This test uses +// ACE_Process.spawn (). (Default) +// +// -f: Measure the performance of native "fork" function +// call. Notice that there is no equivalent NT +// function calls and this option is only available +// on UN*X platform. +// +// -t: Measure the performance of native thread creation +// mechanisms. On Solaris, this is thr_create (). +// On NT, this is CreateThread (). Currently, only +// these two platforms are implemented. +// +// -m: Measure the performance of Thread_Manager::spawn_n +// method. +// +// -x: Test the baseline performance of ACE_Thread_Mutex. +// This really doesn't belong here +// +// -a: Measure the performance of thread creation using +// ACE_OS::thr_create (). +// +// -h: Use High Resolution Timer if supported by platform. +// +// -e: Exec a program after fork (). This option has no +// effect on NT. +// +// = CREATION DATE +// June 29, 1997 +// +// = AUTHOR +// Nanbor Wang +// +// ============================================================================ + +// Process Creation profiling + +#include "ace/OS_NS_unistd.h" +#include "ace/OS_main.h" +#include "ace/Get_Opt.h" +#include "ace/Process.h" +#include "ace/Profile_Timer.h" +#include "ace/Thread_Manager.h" + +ACE_RCSID(Misc, childbirth_time, "$Id$") + +#define ACE_STOP_SIGN ACE_OS::sleep (0) + +#define MAX_NO_ITERATION 10000 +#if defined (ACE_WIN32) +#define SUBPROGRAM ACE_TEXT ("date.exe") +#else +#define SUBPROGRAM ACE_TEXT ("date") +#endif + +size_t MULTIPLY_FACTOR = 10; +typedef double (*Profiler)(size_t); +static int do_exec_after_fork = 0; + +/// do nothing thread function +extern "C" void *ace_empty (void*) +{ + return 0; +} + +static double +prof_ace_process (size_t iteration) +{ + if (iteration != 0) + { + ACE_Process_Options popt; + ACE_Process aProcess; + + popt.command_line (SUBPROGRAM); + + iteration *= MULTIPLY_FACTOR; + + if (do_exec_after_fork == 0) + popt.creation_flags (ACE_Process_Options::NO_EXEC); + + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + pid_t result; + + for (size_t c = 0; c < iteration; c++) + { + ACE_STOP_SIGN; + ptimer.start (); + result = aProcess.spawn (popt); + ptimer.stop (); + + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "process.spawn"), -1); + else if (do_exec_after_fork == 0 && result == 0) + ACE_OS::exit (0) ; + else + { + ptimer.elapsed_time (et); + time += et.real_time; + } + } + + return time / iteration; + } + else + return -1.0; +} + +static double +prof_fork (size_t iteration) +{ +#if !defined (ACE_LACKS_EXEC) + if (iteration != 0) + { + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + + iteration *= MULTIPLY_FACTOR; + + for (size_t i = 0; i < iteration; i++) + { + ACE_STOP_SIGN; + ptimer.start (); + switch (ACE_OS::fork ()) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "process.spawn"), -1); + /* NOTREACHED */ + case 0: + ACE_OS::exit (0); + /* NOTREACHED */ + break; + default: + ptimer.stop (); + ptimer.elapsed_time (et); + time += et.real_time; + } + } + return time / iteration; + } + else + return -1.0; +#else + ACE_UNUSED_ARG (iteration); + ACE_ERROR_RETURN ((LM_ERROR, "fork () is not supported on this platform."), -1); +#endif +} + +static double +prof_native_thread (size_t iteration) +{ +#if defined (ACE_HAS_THREADS) && (defined (ACE_HAS_WTHREADS) || defined (ACE_HAS_STHREADS)) + if (iteration != 0) + { + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + + for (size_t i = 0; i < iteration; i++) + { + ACE_STOP_SIGN; + ptimer.start (); + for (size_t j = 0; j < MULTIPLY_FACTOR; j++) + { +#if defined (ACE_HAS_WTHREADS) + if (::CreateThread (0, + 0, + LPTHREAD_START_ROUTINE (ace_empty), + 0, + CREATE_SUSPENDED, + 0) == 0) +#elif defined (ACE_HAS_STHREADS) + //FUZZ: disable check_for_lack_ACE_OS + if (::thr_create (0, + 0, + &ace_empty, + 0, + THR_SUSPENDED, + 0) != 0) +#endif + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "CreateThread"), -1); + //FUZZ: enable check_for_lack_ACE_OS + } + ptimer.stop (); + ptimer.elapsed_time (et); + time += et.real_time; + } + iteration *= MULTIPLY_FACTOR; + return time / iteration; + } + else + return -1.0; +#else + ACE_UNUSED_ARG (iteration); + ACE_ERROR_RETURN ((LM_ERROR, "Testing of native threads is not supported on this platform.\n"), -1); +#endif +} + +static double +prof_ace_os_thread (size_t iteration) +{ +#if defined (ACE_HAS_THREADS) + if (iteration != 0) + { + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + + for (size_t i = 0; i < iteration; i++) + { + ACE_STOP_SIGN; + ptimer.start (); + + for (size_t j = 0; j < MULTIPLY_FACTOR; j++) + if (ACE_OS::thr_create ((ACE_THR_FUNC) ace_empty, + 0, + THR_SUSPENDED, + 0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "CreateThread"), -1); + + ptimer.stop (); + ptimer.elapsed_time (et); + time += et.real_time; + } + iteration *= MULTIPLY_FACTOR; + return time / iteration; + } + else + return -1.0; +#else + ACE_UNUSED_ARG (iteration); + ACE_ERROR_RETURN ((LM_ERROR, "Threads are not supported on this platform.\n"), -1); +#endif +} + +static double +prof_tm_thread (size_t iteration) +{ +#if defined (ACE_HAS_THREADS) + if (iteration != 0) + { + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + + for (size_t i = 0; i < iteration; i++) + { + ACE_STOP_SIGN; + ptimer.start (); + + if (ACE_Thread_Manager::instance ()->spawn_n (MULTIPLY_FACTOR, + (ACE_THR_FUNC) ace_empty, + 0, + THR_SUSPENDED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "CreateThread"), -1); + + ptimer.stop (); + ptimer.elapsed_time (et); + time += et.real_time; + } + iteration *= MULTIPLY_FACTOR; + return time / iteration; + } + else + return -1.0; +#else + ACE_UNUSED_ARG (iteration); + ACE_ERROR_RETURN ((LM_ERROR, "Threads are not supported on this platform."), -1); +#endif +} + +static double +prof_mutex_base (size_t iteration) +{ +#if defined (ACE_HAS_THREADS) + ACE_Thread_Mutex plain; + if (iteration != 0) + { + ACE_Profile_Timer ptimer; + ACE_Profile_Timer::ACE_Elapsed_Time et; + double time = 0; + + for (size_t i = 0; i < iteration; i++) + { + ACE_STOP_SIGN; + ptimer.start (); + + for (size_t j=0; j < MULTIPLY_FACTOR; j++) + { + plain.acquire (); + plain.release (); + } + + ptimer.stop (); + ptimer.elapsed_time (et); + time += et.real_time; + } + iteration *= MULTIPLY_FACTOR; + return time / iteration; + } + else + return -1.0; +#else + ACE_UNUSED_ARG (iteration); + ACE_ERROR_RETURN ((LM_ERROR, "Threads are not supported on this platform."), -1); +#endif +} + +int +ACE_TMAIN (int argc, ACE_TCHAR* argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("n:l:pftahmxe")); + int c; + size_t iteration = 10; + Profiler profiler = 0; + const char *profile_name = 0 ; + + while ((c=get_opt ()) != -1) + { + switch (c) + { + case 'n': + iteration = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'l': + MULTIPLY_FACTOR = static_cast<size_t> (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'p': // test ACE_Process.spawn () + profiler = prof_ace_process; + profile_name = "ACE_Process.spawn ()"; + break; + case 'f': // test fork () + profiler = prof_fork; + profile_name = "fork ()"; + break; + case 't': // test native thread creation + profiler = prof_native_thread; + profile_name = "native threads"; + break; + case 'a': // test ACE_OS::thr_create + profiler = prof_ace_os_thread; + profile_name = "ACE_OS::thr_create ()"; + break; + case 'm': + profiler = prof_tm_thread; + profile_name = "ACE_Thread_Manager::spawn_n ()"; + break; + case 'x': + profiler = prof_mutex_base; + profile_name = "ACE_Thread_Mutex Baseline"; + break; + case 'h': // use high resolution timer + ACE_High_Res_Timer::get_env_global_scale_factor (); + break; + case 'e': + do_exec_after_fork = 1; + break; + default: + break; + } + } + + if (profiler == 0) + ACE_ERROR_RETURN ((LM_ERROR, "Usage: childbirth_time {-p|-f|-t|-a|-m|-x} [-n ###] [-L ###] [-h] [-e]\n"), 1); + else + { + double time = profiler (iteration); + if (time > 0) + ACE_DEBUG ((LM_DEBUG, + "Average performance of %d iterations of %s: %.0f usec\n", + iteration * MULTIPLY_FACTOR, profile_name, time * 1e6)); + } + return 0; +} diff --git a/ACE/performance-tests/Misc/context_switch_time.cpp b/ACE/performance-tests/Misc/context_switch_time.cpp new file mode 100644 index 00000000000..70e9a0610a0 --- /dev/null +++ b/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 +ACE_TMAIN(int, ACE_TCHAR *[]) +{ + ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n")); + return 0; +} +#endif /* ACE_HAS_THREADS */ + + +// EOF diff --git a/ACE/performance-tests/Misc/preempt.cpp b/ACE/performance-tests/Misc/preempt.cpp new file mode 100644 index 00000000000..0e6edcfbc30 --- /dev/null +++ b/ACE/performance-tests/Misc/preempt.cpp @@ -0,0 +1,467 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance-tests/Misc +// +// = FILENAME +// preempt.cpp +// +// = DESCRIPTION +// This is a simple test to illustrate OS thread preemption. One +// ore more high priority threads periodically (every half +// second, by default) reads the clock. They use select () to +// block for that duration. Meanwhile, a low priority thread +// continually chews up the CPU. Without preemption, the high +// priority thread won't have a chance to read the clock in a +// timely manner. +// +// At the end of the test, the actual clock read intervals by the +// high priority task(s) are printed out. With proper +// preemption, the intervals should correspond to the requested +// clock read interval. +// +// There is a -y option for the low priority thread to periodically +// yield. It shouldn't be necessary to use that option, if preemption +// is supported. It's a handy option for testing. +// +// = AUTHOR +// David L. Levine +// +// ============================================================================ + +#include "ace/OS_main.h" +#include "ace/ACE.h" +#include "ace/Task.h" +#include "ace/Sched_Params.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_time.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(Misc, preempt, "$Id$") + +#if defined (ACE_HAS_THREADS) || ! defined (ACE_LACKS_FORK) + +#if defined (ACE_HAS_STHREADS) +# include <sys/lwp.h> /* for _lwp_self () */ +#endif /* ACE_HAS_STHREADS */ + +static const char usage [] = "[-? |\n" +#if defined (ACE_HAS_THREADS) + " [-f use fork instead of spawn]\n" +#endif /* ACE_HAS_THREADS */ + " [-h <high pri iterations, default 10>]\n" + " [-l <low pri iterations, default 25000>]\n" + " [-n <high priority threads, default 1>]\n" + " [-p <read period, default 500000 usec>]\n" + " [-y to yield the low priority thread]]\n"; + +// Configuration options. +#if defined (ACE_HAS_THREADS) +u_int use_fork = 0; +#else /* ! ACE_HAS_THREADS */ +u_int use_fork = 1; +#endif /* ! ACE_HAS_THREADS */ +u_int high_iterations = 10; +u_int high_priority_tasks = 1; +u_int low_iterations = 25000; /* Big enough to keep the low priority task + cranking for a while */ +u_long read_period = 500000; /* usec */ +u_int low_yield = 0; + +// To permit calculation of relative times. +ACE_hrtime_t starttime; + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class High_Priority_Task +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class High_Priority_Task : public ACE_Task<ACE_SYNCH> +{ +public: + High_Priority_Task (void); + ~High_Priority_Task (void); + + //FUZZ: disable check_for_lack_ACE_OS + int open (void *); + //FUZZ: enable check_for_lack_ACE_OS + + int svc (void); + int done () const { return done_; } + void print_times () const; + +private: + int priority_; + int done_; + u_long *time_; +}; + +High_Priority_Task::High_Priority_Task (void) + : ACE_Task<ACE_SYNCH> (ACE_Thread_Manager::instance ()), + priority_ (ACE_Sched_Params::next_priority ( + ACE_SCHED_FIFO, + ACE_Sched_Params::priority_min (ACE_SCHED_FIFO, + ACE_SCOPE_THREAD), + ACE_SCOPE_THREAD)), + done_ (0) +{ + ACE_NEW (time_, u_long[high_iterations]); +} + +High_Priority_Task::~High_Priority_Task (void) +{ + delete [] time_; + time_ = 0; +} + +int +High_Priority_Task::open (void *) +{ + if (use_fork == 1) + { + ACE_hthread_t thr_handle; + ACE_Thread::self (thr_handle); + + if (ACE_Thread::setprio (thr_handle, priority_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "setprio failed"), -1); + + return svc (); + } + else + { + long flags = THR_BOUND | THR_NEW_LWP | THR_SCHED_FIFO; + + // Become an active object. + if (this->activate (flags, 1, 0, this->priority_) == -1) + { + ACE_DEBUG ((LM_ERROR, "(%P|%t) task activation failed, exiting!\n")); + ACE_OS::exit (1); + } + + return 0; + } +} + +int +High_Priority_Task::svc (void) +{ + ACE_hthread_t thr_handle; + ACE_Thread::self (thr_handle); + int prio; + + if (ACE_Thread::getprio (thr_handle, prio) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getprio failed"), -1); + +#if defined (ACE_HAS_STHREADS) + ACE_DEBUG ((LM_DEBUG, "(%P|%u|%t) High: prio = %d, priority_ = %d\n", + _lwp_self (), prio, this->priority_)); +#else /* ! ACE_HAS_STHREADS */ + ACE_DEBUG ((LM_DEBUG, "(%P|%t) High: prio = %d, priority_ = %d\n", + prio, this->priority_)); +#endif /* ! ACE_HAS_STHREADS */ + ACE_ASSERT (this->priority_ == prio); + + ACE_Time_Value pause (0, read_period); + + for (u_int i = 0; i < high_iterations; ++i) + { + time_ [i] = (u_long) ((ACE_OS::gethrtime () - starttime)/ + (ACE_UINT32) 1000000u); + ACE_OS::select (0, 0, 0, 0, &pause); + } + + done_ = 1; + + return 0; +} + +void +High_Priority_Task::print_times () const +{ + for (u_int i = 0; i < high_iterations; ++i) + { + ACE_DEBUG ((LM_INFO, " iteration %u, time %u msec\n", + i, time_ [i])); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// class Low_Priority_Task +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class Low_Priority_Task : public ACE_Task<ACE_SYNCH> +{ +public: + Low_Priority_Task (const High_Priority_Task &); + + //FUZZ: disable check_for_lack_ACE_OS + int open (void *); + //FUZZ: enable check_for_lack_ACE_OS + + int svc (void); + +private: + int priority_; + const High_Priority_Task &high_priority_task_; +}; + +Low_Priority_Task::Low_Priority_Task ( + const High_Priority_Task &high_priority_task) + : ACE_Task<ACE_SYNCH> (ACE_Thread_Manager::instance ()), + priority_ (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO, + ACE_SCOPE_THREAD)), + high_priority_task_ (high_priority_task) +{ +} + +int +Low_Priority_Task::open (void *) +{ + if (use_fork == 1) + { + ACE_hthread_t thr_handle; + ACE_Thread::self (thr_handle); + + if (ACE_Thread::setprio (thr_handle, priority_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "setprio failed"), -1); + + return svc (); + } + else + { + long flags = THR_BOUND | THR_NEW_LWP | THR_SCHED_FIFO; + + // Become an active object. + if (this->activate (flags, 1, 0, this->priority_) == -1) + ACE_ERROR ((LM_ERROR, + "(%P|%t) task activation failed, exiting!\n%a", + -1)); + return 0; + } +} + +int +Low_Priority_Task::svc (void) +{ + ACE_hthread_t thr_handle; + ACE_Thread::self (thr_handle); + int prio; + + if (ACE_Thread::getprio (thr_handle, prio) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "getprio failed"), -1); + +#if defined (ACE_HAS_STHREADS) + ACE_DEBUG ((LM_DEBUG, "(%P|%u|%t) Low: prio = %d, priority_ = %d\n", + _lwp_self (), prio, this->priority_)); +#else /* ! ACE_HAS_STHREADS */ + ACE_DEBUG ((LM_DEBUG, "(%P|%t) Low: prio = %d, priority_ = %d\n", + prio, this->priority_)); +#endif /* ! ACE_HAS_STHREADS */ + ACE_ASSERT (this->priority_ == prio); + + u_long iterations = 0; + + // Chew up CPU for the entire interval. + for (; + ! high_priority_task_.done () && iterations < low_iterations; + ++iterations) + { + u_long n = 1279ul; /* Takes about 40.2 usecs on a 168 MHz Ultra2. */ + ACE::is_prime (n, + 2ul /* min_factor */, + n/2 /* max_factor */); + + if (low_yield) + ACE_OS::thr_yield (); + } + + ACE_DEBUG ((LM_INFO, "Low completed %u iterations\n", iterations)); + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function get_options +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int +get_options (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("fh:l:n:p:y?")); + int opt; + while ((opt = get_opt ()) != EOF) { + switch (opt) { + case 'f': + use_fork = 1; + break; + case 'h': + if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2) + high_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + else + ACE_ERROR_RETURN ((LM_ERROR, "%n: high iterations must be >= 2\n"), + -1); + break; + case 'l': + if (ACE_OS::atoi (get_opt.opt_arg ()) >= 2) + low_iterations = ACE_OS::atoi (get_opt.opt_arg ()); + else + ACE_ERROR_RETURN ((LM_ERROR, "%n: low iterations must be >= 2\n"), -1); + break; + case 'n': + if (ACE_OS::atoi (get_opt.opt_arg ()) >= 1) + high_priority_tasks = ACE_OS::atoi (get_opt.opt_arg ()); + else + ACE_ERROR_RETURN ((LM_ERROR, "%n: number of high priority threads " + "must be >= 1\n"), -1); + break; + case 'p': + if (ACE_OS::atoi (get_opt.opt_arg ()) > 0) + read_period = ACE_OS::atoi (get_opt.opt_arg ()); + else + ACE_ERROR_RETURN ((LM_ERROR, "%n: read period > 0\n"), -1); + break; + case 'y': + low_yield = 1; + break; + case '?': + ACE_DEBUG ((LM_ERROR, "usage: %n %s\n%a", usage, 0)); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n: unknown arg, %c\nusage: %n %s\n", opt, usage), + -1); + } + } + + switch (argc - get_opt.opt_ind ()) { + case 0: + // OK, no non-flag arguments. + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "%n: too many arguments\nusage: %n %s\n", usage), + -1); + } + + return 0; +} + +#endif /* ACE_HAS_THREADS || ! ACE_LACKS_FORK */ + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// function main +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_LOG_MSG->open (argv[0] ? argv[0] : ACE_TEXT("preempt")); + +#if defined (ACE_HAS_THREADS) || !defined (ACE_LACKS_FORK) + + u_int i; + + if (get_options (argc, argv)) + ACE_OS::exit (-1); + + // Enable FIFO scheduling, e.g., RT scheduling class on Solaris. + 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, "preempt: user is not superuser, " + "so remain in time-sharing class\n")); + else + ACE_ERROR_RETURN ((LM_ERROR, "%n: ACE_OS::sched_params failed\n%a"), + -1); + } + + High_Priority_Task *high_priority_task; + ACE_NEW_RETURN (high_priority_task, High_Priority_Task [high_priority_tasks], + 1); + + Low_Priority_Task low_priority_task (high_priority_task[0]); + + if (! use_fork) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) main (), wait for threads to exit . . .\n")); + } + + // Save the start time, so that deltas can be displayed later. + starttime = ACE_OS::gethrtime (); + + // Spawn the threads/processes . . . + pid_t child = 0; + if (use_fork == 1) + { + switch ((child = ACE_OS::fork (ACE_TEXT("preempt-low_priority_process")))) + { + case -1: + ACE_ERROR ((LM_ERROR, "%p\n%a", "fork failed")); + return -1; + case 0: // In child + { + low_priority_task.open (0); + break; + } + default: // In parent + for (i = 0; i < high_priority_tasks; ++i) + { + high_priority_task[i].open (0); + } + break; + } + } + else + { + for (i = 0; i < high_priority_tasks; ++i) + { + high_priority_task[i].open (0); + } + low_priority_task.open (0); + +#if defined (ACE_HAS_THREADS) + // Wait for all threads to exit. + ACE_Thread_Manager::instance ()->wait (); +#endif /* ACE_HAS_THREADS */ + } + + // Display the time deltas. They should be about a half second apart. + if (child || ! use_fork) + { + for (i = 0; i < high_priority_tasks; ++i) + { + ACE_DEBUG ((LM_DEBUG, "High priority task %u:\n", i + 1)); + high_priority_task[i].print_times (); + } + } + + delete [] high_priority_task; + +#else /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + ACE_ERROR ((LM_ERROR, "threads and fork not supported on this platform\n")); +#endif /* ! ACE_HAS_THREADS && ACE_LACKS_FORK */ + + return 0; +} diff --git a/ACE/performance-tests/Misc/test_guard.cpp b/ACE/performance-tests/Misc/test_guard.cpp new file mode 100644 index 00000000000..11c28741405 --- /dev/null +++ b/ACE/performance-tests/Misc/test_guard.cpp @@ -0,0 +1,119 @@ +// $Id$ + +// This test program illustrates the performance of ACE_Guard and +// ACE_Thread_Mutex_Guard. + +#include "ace/Log_Msg.h" +#include "ace/Get_Opt.h" +#include "ace/Profile_Timer.h" + +#if !defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +#error You must compile ACE and this program with ACE_USES_OBSOLETE_GUARD_CLASSES defined! +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + +ACE_RCSID(Misc, test_guard, "$Id$") + +#if defined (ACE_HAS_THREADS) + +# define ACE_THREAD_GUARD(OBJ,LOCK) \ + ACE_Thread_Mutex_Guard OBJ (LOCK); \ + if (OBJ.locked () == 0) return; + +static const int DEFAULT_ITERATIONS = 100000000; + +enum +{ + TEST_GUARD, + TEST_THR_GUARD, + TEST_END +}; + +ACE_Thread_Mutex lock_; +typedef void (*guard_func)(void); +int test_type = TEST_GUARD; +int dummy = 0; + +void guard (void) +{ + ACE_GUARD (ACE_Thread_Mutex, _ace_mon, lock_); + dummy++; +} + +void thr_guard (void) +{ + ACE_THREAD_GUARD (_ace_mon, lock_); + dummy++; +} + +char *test_name[TEST_END] = { "ACE_Guard", "ACE_Thread_Mutex_Guard" }; + +guard_func test_function=guard; + +int +ACE_TMAIN(int argc, ACE_TCHAR *argv[]) +{ + ACE_Profile_Timer timer; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, "gtn:"); + //FUZZ: enable check_for_lack_ACE_OS + + int iterations = DEFAULT_ITERATIONS; + int c, i; + + //FUZZ: disable check_for_lack_ACE_OS + while ((c = getopt()) != -1) + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) + case 't': + test_type = TEST_THR_GUARD; + test_function = thr_guard; + break; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + case 'g': + test_type = TEST_GUARD; + test_function = guard; + break; + case 'n': + iterations = ACE_OS::atoi (getopt.opt_arg ()); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "Invalid option\n"), -1); + } + + ACE_DEBUG ((LM_DEBUG, "%s for iterations = %d\n", test_name[test_type], iterations)); + + timer.start (); + + // Test the thread mutex (which doesn't use inheritance or dynamic + // binding). + + for (i = 0; i < iterations; i++) + test_function (); + + 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)); + + return 0; +} +#else +int +ACE_TMAIN(int, ACE_TCHAR *[]) +{ + ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n")); + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Misc/test_mutex.cpp b/ACE/performance-tests/Misc/test_mutex.cpp new file mode 100644 index 00000000000..cff5dd76249 --- /dev/null +++ b/ACE/performance-tests/Misc/test_mutex.cpp @@ -0,0 +1,238 @@ +// $Id$ + +// This test program illustrates the performance difference between +// 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_main.h" +#include "ace/OS_NS_Thread.h" + +ACE_RCSID(Misc, test_mutex, "$Id$") + +#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 ~Mutex_Base (void); + virtual int acquire (void) = 0; + virtual int release (void) = 0; +}; + +Mutex_Base::~Mutex_Base (void) +{ +} + +// Subclass for threaded mutex, defines virtual functions. +class Thread_Mutex_Derived : public Mutex_Base +{ +public: + Thread_Mutex_Derived (void); + virtual ~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 +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)); + + 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 +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n")); + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Misc/test_naming.cpp b/ACE/performance-tests/Misc/test_naming.cpp new file mode 100644 index 00000000000..01938e7e563 --- /dev/null +++ b/ACE/performance-tests/Misc/test_naming.cpp @@ -0,0 +1,199 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance_tests +// +// = FILENAME +// test_naming.cpp +// +// = DESCRIPTION +// This is an example to do performance testing of the Naming Service +// using both the normal Memory Pool as well as the light Memory Pool. +// +// = AUTHOR +// Prashant Jain +// +// ============================================================================ + +#include "ace/OS_main.h" +#include "ace/ACE.h" +#include "ace/SString.h" +#include "ace/Naming_Context.h" +#include "ace/Profile_Timer.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_stdio.h" + +ACE_RCSID(Misc, test_naming, "$Id$") + + +#define ACE_NS_MAX_ENTRIES 4000 + +static char name[BUFSIZ]; +static char value[BUFSIZ]; +static char type[BUFSIZ]; + +//FUZZ: disable check_for_lack_ACE_OS +void +bind (ACE_Naming_Context *ns_context, int result) +{ +//FUZZ: enable check_for_lack_ACE_OS + + // do the binds + for (int i = 1; i <= ACE_NS_MAX_ENTRIES; i++) + { + if (i % 50 == 0) + ACE_DEBUG ((LM_DEBUG, ".")); + ACE_OS::sprintf (name, "%s%d", "name", i); + ACE_NS_WString w_name (name); + + ACE_OS::sprintf (value, "%s%d", "value", i); + ACE_NS_WString w_value (value); + + ACE_OS::sprintf (type, "%s%d", "type", i); + if (ns_context->bind (w_name, w_value, type) != result) { + ACE_ERROR ((LM_ERROR, "bind failed!")); + } + } + ACE_DEBUG ((LM_DEBUG, "\n")); +} + +void +rebind (ACE_Naming_Context *ns_context, int result) +{ + // do the rebinds + for (int i = 1; i <= ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", i); + ACE_NS_WString w_name (name); + ACE_OS::sprintf (value, "%s%d", "value", -i); + ACE_NS_WString w_value (value); + ACE_OS::sprintf (type, "%s%d", "type", -i); + if (ns_context->rebind (w_name, w_value, type) != result) { + ACE_ERROR ((LM_ERROR, "rebind failed!")); + } + } +} + +void +unbind (ACE_Naming_Context *ns_context, int result) +{ + // do the unbinds + for (int i = 1; i <= ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", i); + ACE_NS_WString w_name (name); + if (ns_context->unbind (w_name) != result) { + ACE_ERROR ((LM_ERROR, "unbind failed!")); + } + } +} + +void +find (ACE_Naming_Context *ns_context, int sign, int result) +{ + char temp_val[BUFSIZ]; + char temp_type[BUFSIZ]; + + // do the finds + for (int i = 1; i <= ACE_NS_MAX_ENTRIES; i++) + { + ACE_OS::sprintf (name, "%s%d", "name", i); + ACE_NS_WString w_name (name); + + ACE_NS_WString w_value; + char *type_out; + + if (sign == 1) + { + ACE_OS::sprintf (temp_val, "%s%d", "value", i); + ACE_OS::sprintf (temp_type, "%s%d", "type", i); + } + else + { + ACE_OS::sprintf (temp_val, "%s%d", "value", -i); + ACE_OS::sprintf (temp_type, "%s%d", "type", -i); + } + + ACE_NS_WString val (temp_val); + + int resolve_result = ns_context->resolve (w_name, w_value, type_out); + if (resolve_result != result) { + ACE_ERROR ((LM_ERROR, "resolved failed!")); + } + ACE_ASSERT (resolve_result == result); + ACE_UNUSED_ARG (resolve_result); // To avoid compile warning + // with ACE_NDEBUG. + + if (w_value.char_rep ()) + { + ACE_DEBUG ((LM_DEBUG, "Name: %s\tValue: %s\tType: %s\n", + name, w_value.char_rep (), type_out)); + ACE_ASSERT (w_value == val); + if (type_out) + { + ACE_ASSERT (ACE_OS::strcmp (type_out, temp_type) == 0); + delete[] type_out; + } + } + } +} + +void do_testing (int argc, ACE_TCHAR *argv[], int light) +{ + ACE_Profile_Timer timer; + + ACE_Naming_Context ns_context; + ACE_Name_Options *name_options = ns_context.name_options (); + name_options->parse_args (argc, argv); + + if (light == 0) // Use SYNC + { + name_options->database (ACE::basename (name_options->process_name (), + ACE_DIRECTORY_SEPARATOR_CHAR)); + ns_context.open (ACE_Naming_Context::PROC_LOCAL); + } + else // Use NO-SYNC + { + const ACE_TCHAR *p = ACE::basename (name_options->process_name (), + ACE_DIRECTORY_SEPARATOR_CHAR); + ACE_TCHAR s[5 /* strlen ("light") */ + MAXNAMELEN + 1]; + ACE_OS::sprintf (s, ACE_TEXT("light%s"), p); + name_options->database (s); + ns_context.open (ACE_Naming_Context::PROC_LOCAL, 1); + } + + // Add bindings to the database + ACE_DEBUG ((LM_DEBUG, "Binding\n")); + + timer.start (); + + //FUZZ: disable check_for_lack_ACE_OS + bind (&ns_context, 0); + //FUZZ: enable check_for_lack_ACE_OS + + ACE_DEBUG ((LM_DEBUG, "Unbinding\n")); + unbind (&ns_context, 0); + timer.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time et; + + timer.elapsed_time (et); + 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)); +} + + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + // Do testing with SYNC on + ACE_DEBUG ((LM_DEBUG, "SYNC is ON\n")); + do_testing (argc, argv, 0); + + // Do testing with SYNC off + ACE_DEBUG ((LM_DEBUG, "SYNC is OFF\n")); + do_testing (argc, argv, 1); + return 0; +} diff --git a/ACE/performance-tests/Misc/test_singleton.cpp b/ACE/performance-tests/Misc/test_singleton.cpp new file mode 100644 index 00000000000..53790a0fca4 --- /dev/null +++ b/ACE/performance-tests/Misc/test_singleton.cpp @@ -0,0 +1,179 @@ +// $Id$ + +// 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" + +ACE_RCSID(Misc, test_singleton, "$Id$") + +#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 */ diff --git a/ACE/performance-tests/Misc/test_singleton.h b/ACE/performance-tests/Misc/test_singleton.h new file mode 100644 index 00000000000..70aef65d89b --- /dev/null +++ b/ACE/performance-tests/Misc/test_singleton.h @@ -0,0 +1,24 @@ +// $Id$ + +// Define the DC_Singleton here - it needs to be in a separate file to +// get picked up correctly on AIX with auto template instantiation using +// IBM C++ compiler (xlC). + +#ifndef __TEST_SINGLETON_H +#define __TEST_SINGLETON_H + +#include "ace/Synch_Traits.h" + +class DC_Singleton +{ +public: + DC_Singleton (void) {} + void svc (void) {} + static DC_Singleton *instance (void); + +private: + static ACE_SYNCH_MUTEX lock_; + static DC_Singleton *instance_; +}; + +#endif /* __TEST_SINGLETON_H */ |