diff options
author | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
---|---|---|
committer | William R. Otte <wotte@dre.vanderbilt.edu> | 2008-03-04 14:51:23 +0000 |
commit | 99aa8c60282c7b8072eb35eb9ac815702f5bf586 (patch) | |
tree | bda96bf8c3a4c2875a083d7b16720533c8ffeaf4 /ACE/performance-tests | |
parent | c4078c377d74290ebe4e66da0b4975da91732376 (diff) | |
download | ATCD-99aa8c60282c7b8072eb35eb9ac815702f5bf586.tar.gz |
undoing accidental deletion
Diffstat (limited to 'ACE/performance-tests')
141 files changed, 18734 insertions, 0 deletions
diff --git a/ACE/performance-tests/Makefile.am b/ACE/performance-tests/Makefile.am new file mode 100644 index 00000000000..8893d7354cb --- /dev/null +++ b/ACE/performance-tests/Makefile.am @@ -0,0 +1,18 @@ +## 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 + +SUBDIRS = \ + Misc \ + SCTP \ + Server_Concurrency \ + Synch-Benchmarks \ + TCP \ + UDP + 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 */ diff --git a/ACE/performance-tests/README b/ACE/performance-tests/README new file mode 100644 index 00000000000..36751c061fe --- /dev/null +++ b/ACE/performance-tests/README @@ -0,0 +1,16 @@ +The subdirectories in this directory provide a number of benchmarks +that use ACE features to determine performance of various concurrency +and communication mechanisms. + + . Synch-Benchmarks -- Implements a number of benchmarks + that test the performance of various synchronization + mechanisms. + + . TTCP -- Implements several variants of the TTCP benchmarking + test for TCP and UCP using C sockets and ACE C++ wrappers. + + . UDP -- Contains UDP test, which measures UDP round-trip + performance. + + . Misc -- Miscellaneous tests, e.g., Double-Checked Locking, + context switching, mutexes, naming, etc. diff --git a/ACE/performance-tests/RPC/README b/ACE/performance-tests/RPC/README new file mode 100644 index 00000000000..49b9131733e --- /dev/null +++ b/ACE/performance-tests/RPC/README @@ -0,0 +1,6 @@ +# $Id$ + + Measure latency over RPC, run as: + +$ server +$ client -n <samples> -k host diff --git a/ACE/performance-tests/RPC/RPC.mpc b/ACE/performance-tests/RPC/RPC.mpc new file mode 100644 index 00000000000..feccec74ce5 --- /dev/null +++ b/ACE/performance-tests/RPC/RPC.mpc @@ -0,0 +1,17 @@ +// -*- MPC -*- +// $Id$ + +project(*client) : aceexe, rpc { + source_files { + client.cpp + ping_clnt.c + } +} + +project(*server) : aceexe, rpc { + exename = server + source_files { + server.c + ping_svc.c + } +}
\ No newline at end of file diff --git a/ACE/performance-tests/RPC/client.cpp b/ACE/performance-tests/RPC/client.cpp new file mode 100644 index 00000000000..b3d3982920c --- /dev/null +++ b/ACE/performance-tests/RPC/client.cpp @@ -0,0 +1,74 @@ +/* + * $Id$ + */ +#include "ace/Stats.h" +#include "ace/High_Res_Timer.h" +#include "ace/Get_Opt.h" + +#include "ping.h" + +int main (int argc, char* argv[]) +{ + const char* host = 0; + int nsamples = 10000; + int c; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, "h:i:"); + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch ((char) c) + { + case 'h': + host = getopt.opt_arg (); + break; + + case 'i': + nsamples = ACE_OS::atoi (getopt.opt_arg ()); + break; + } + } + + if (host == 0) + { + ACE_DEBUG ((LM_DEBUG, "Usage: client -h host -i iterations\n")); + return 1; + } + + CLIENT *cl = + clnt_create (host, PINGPROG, PINGVERS, "tcp"); + + if (cl == 0) + { + ACE_DEBUG ((LM_DEBUG, "Cannot create client handle\n")); + return 1; + } + + ACE_Throughput_Stats throughput; + + ACE_hrtime_t test_start = ACE_OS::gethrtime (); + for (int i = 0; i != nsamples; ++i) + { + ACE_hrtime_t start = ACE_OS::gethrtime (); + + int p = 0; + (void) ping_1 (&p, cl); + + ACE_hrtime_t end = ACE_OS::gethrtime (); + + throughput.sample (end - test_start, + end - start); + + } + + ACE_DEBUG ((LM_DEBUG, "Calibrating high resolution timer . . .")); + ACE_UINT32 gsf = ACE_High_Res_Timer::global_scale_factor (); + ACE_DEBUG ((LM_DEBUG, " done\n")); + + throughput.dump_results ("Client", gsf); + + + return 0; +} diff --git a/ACE/performance-tests/RPC/ping.x b/ACE/performance-tests/RPC/ping.x new file mode 100644 index 00000000000..ea078ffe139 --- /dev/null +++ b/ACE/performance-tests/RPC/ping.x @@ -0,0 +1,12 @@ +/* + * $Id$ + */ + +/* + * A small program to test RPC round-trip delays. + */ +program PINGPROG { + version PINGVERS { + int PING (int) = 1; + } = 1; +} = 0x20000001; diff --git a/ACE/performance-tests/RPC/run_test.pl b/ACE/performance-tests/RPC/run_test.pl new file mode 100755 index 00000000000..305509dc83a --- /dev/null +++ b/ACE/performance-tests/RPC/run_test.pl @@ -0,0 +1,34 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- + +use lib '../../bin'; +use PerlACE::Run_Test; + +$SV = new PerlACE::Process ("server"); +$CL = new PerlACE::Process ("client", "-i 10000 -h localhost"); + +$status = 0; + +$SV->Spawn (); + +sleep 5; + +$client = $CL->SpawnWaitKill (60); + +$server = $SV->WaitKill (5); + +if ($server != 0) { + print "ERROR: server returned $server\n"; + $status = 1; +} + +if ($client != 0) { + print "ERROR: client returned $client\n"; + $status = 1; +} + +exit $status; diff --git a/ACE/performance-tests/RPC/server.c b/ACE/performance-tests/RPC/server.c new file mode 100644 index 00000000000..2f8526a6733 --- /dev/null +++ b/ACE/performance-tests/RPC/server.c @@ -0,0 +1,15 @@ +/* + * $Id$ + */ + +#include "ping.h" +#include <rpc/rpc.h> +#include <stdio.h> + +static int return_value = 0; + +int* ping_1_svc (int* value, struct svc_req* r) +{ + return_value = *value; + return &return_value; +} diff --git a/ACE/performance-tests/SCTP/Makefile.am b/ACE/performance-tests/SCTP/Makefile.am new file mode 100644 index 00000000000..31ae3d50200 --- /dev/null +++ b/ACE/performance-tests/SCTP/Makefile.am @@ -0,0 +1,122 @@ +## 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.SCTP_SOCK_SEQPACK_Association_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_SEQPACK_Association_Test + +SOCK_SEQPACK_Association_Test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_SEQPACK_Association_Test_SOURCES = \ + SOCK_SEQPACK_Association_Test.cpp \ + Options_Manager.h \ + hist.h + +SOCK_SEQPACK_Association_Test_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SCTP_SOCK_SEQPACK_Clt.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_SEQPACK_clt + +SOCK_SEQPACK_clt_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_SEQPACK_clt_SOURCES = \ + Options_Manager.cpp \ + SOCK_SEQPACK_clt.cpp \ + hist.cpp \ + Options_Manager.h \ + hist.h + +SOCK_SEQPACK_clt_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SCTP_SOCK_SEQPACK_Srv.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_SEQPACK_srv + +SOCK_SEQPACK_srv_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_SEQPACK_srv_SOURCES = \ + Options_Manager.cpp \ + SOCK_SEQPACK_srv.cpp \ + Options_Manager.h + +SOCK_SEQPACK_srv_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SCTP_SOCK_STREAM_Clt.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += SOCK_STREAM_clt + +SOCK_STREAM_clt_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_STREAM_clt_SOURCES = \ + Options_Manager.cpp \ + SOCK_STREAM_clt.cpp \ + hist.cpp \ + Options_Manager.h \ + hist.h + +SOCK_STREAM_clt_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.SCTP_SOCK_STREAM_Srv.am + +noinst_PROGRAMS += SOCK_STREAM_srv + +SOCK_STREAM_srv_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +SOCK_STREAM_srv_SOURCES = \ + Options_Manager.cpp \ + SOCK_STREAM_srv.cpp \ + Options_Manager.h + +SOCK_STREAM_srv_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## 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/SCTP/Options_Manager.cpp b/ACE/performance-tests/SCTP/Options_Manager.cpp new file mode 100644 index 00000000000..679249ebf15 --- /dev/null +++ b/ACE/performance-tests/SCTP/Options_Manager.cpp @@ -0,0 +1,542 @@ +// -*- C++ -*- +// $Id$ + +#include "ace/Get_Opt.h" +#include "ace/os_include/netinet/os_in.h" +#include "ace/os_include/sys/os_types.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" + +// make sure that the code compiles cleanly even if SCTP is not +// available. If SCTP is not installed, program will exit early in +// main() with an error message. +#ifdef ACE_HAS_SCTP +extern "C" { +#include <netinet/sctp.h> +}; +#else +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 /* always the same value on every platform */ +#endif +#endif + +#include "Options_Manager.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_arpa_inet.h" + +// Set default values +ACE_CDR::ULong Options_Manager::test_iterations=1000000; +ACE_CDR::Boolean Options_Manager::test_enable_nagle=0; +ACE_CDR::Long Options_Manager::test_transport_protocol=IPPROTO_SCTP; +ACE_CDR::Double Options_Manager::histogram_min_bin=0.0; +ACE_CDR::Double Options_Manager::histogram_max_bin=10000.0; +ACE_CDR::ULong Options_Manager::histogram_num_outliers=100; +ACE_CDR::ULong Options_Manager::histogram_bin_count=1000; +ACE_CDR::UShort Options_Manager::client_port = 0; +ACE_CDR::ULong Options_Manager::client_connect_addr=INADDR_ANY; +ACE_CDR::UShort Options_Manager::server_port = 45453; +ACE_TCHAR Options_Manager::server_host[Options_Manager::string_len]; +ACE_CDR::ULong Options_Manager::server_accept_addr=INADDR_ANY; +ACE_CDR::UShort Options_Manager::payload_size_power_of_2=31; +ACE_CDR::ULong Options_Manager::secondary_connect_addrs[max_num_secondary_connect_addrs]; +ACE_CDR::UShort Options_Manager::num_secondary_connect_addrs = 0; +ACE_CDR::ULong Options_Manager::secondary_accept_addrs[max_num_secondary_accept_addrs]; +ACE_CDR::UShort Options_Manager::num_secondary_accept_addrs = 0; +ACE_CDR::UShort Options_Manager::_usage = 0; +ACE_CDR::UShort Options_Manager::_error = 0; +ACE_TCHAR Options_Manager::__program_name[Options_Manager::string_len]; +ACE_CDR::Boolean Options_Manager::__initialized=0; +const ACE_TCHAR* Options_Manager::_error_message; + + +Options_Manager::Options_Manager(int argc, ACE_TCHAR **argv, ACE_TCHAR const * const opts_set) +{ + if (!__initialized) { + // Set default values that were not set during static initialization + ACE_OS::strcpy(server_host, ACE_LOCALHOST); + + // Remember argv[0] + if (ACE_OS::strlen(argv[0]) < Options_Manager::string_len) + ACE_OS::strcpy(__program_name, argv[0]); + else{ + ACE_OS::strncpy(__program_name, argv[0], (Options_Manager::string_len-1)); + __program_name[Options_Manager::string_len - 1] = '\0'; + } + + // Declare options with ACE_Get_Opt + int c; + ACE_Get_Opt * get_opt = 0; + if (!ACE_OS::strcmp(ACE_TEXT ("client-opts"), opts_set)){ + get_opt = new ACE_Get_Opt(argc, argv, ACE_TEXT("c:nt:m:M:x:b:C:i:p:H:s:h")); + + get_opt->long_option (ACE_TEXT ("test_iterations"), 'c', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("test_enable_nagle"), 'n'); + get_opt->long_option (ACE_TEXT ("test_transport_protocol"), 't', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("histogram_min_bin"), 'm', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("histogram_max_bin"), 'M', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("histogram_num_outliers"), 'x', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("histogram_bin_count"), 'b', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("client_port"), 'C', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("client_accept_addr"), 'i', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("server_port"), 'p', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("server_host"), 'H', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("payload_size_power_of_2"), 's', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("help"), 'h'); + } else if (!ACE_OS::strcmp (ACE_TEXT ("server-opts"), opts_set)){ + get_opt = new ACE_Get_Opt(argc, argv, ACE_TEXT("nt:p:a:u")); + get_opt->long_option (ACE_TEXT ("test_enable_nagle"), 'n'); + get_opt->long_option (ACE_TEXT ("test_transport_protocol"), 't', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("server_port"), 'p', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("server_accept_addr"), 'a', + ACE_Get_Opt::ARG_REQUIRED); + get_opt->long_option (ACE_TEXT ("help"), 'h'); + } else { + _error = 1; + _error_message = ACE_TEXT ("invalid options set specified"); + delete get_opt; + return; + } + + // Parse options with ACE_Get_Opt + while ((c = (*get_opt)()) != -1) + { + switch ((char) c) + { + case 'c': + test_iterations = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'n': + test_enable_nagle = 1; + break; + case 't':{ + ACE_TCHAR const * const str = get_opt->opt_arg (); + if (!ACE_OS::strcmp (str, ACE_TEXT ("tcp"))) + test_transport_protocol = IPPROTO_TCP; + else if (!ACE_OS::strcmp (str, ACE_TEXT ("sctp"))) + test_transport_protocol = IPPROTO_SCTP; + else + test_transport_protocol = -1; + break; + } + case 'm': + histogram_min_bin = ACE_OS::strtod(get_opt->opt_arg (), 0); + break; + case 'M': + histogram_max_bin = ACE_OS::strtod(get_opt->opt_arg (), 0); + break; + case 'x': + histogram_num_outliers = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'b': + histogram_bin_count = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'C': + client_port = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'i':{ + + // The argument to this option is a comma-separated list + // of dotted-decimal ipv4 addresses. + + // Create a writable copy of the options argument + ACE_TCHAR str[Options_Manager::string_len]; + ACE_OS::strncpy(str, get_opt->opt_arg(), Options_Manager::string_len); + + // Get a pointer to the first comma in the list + ACE_TCHAR *next_secondary_addr = ACE_OS::strchr(str, ','); + + // If found, the comma is replaced with \0 and pointer + // updated to point to the string that begins immediately + // after the comma. + if (next_secondary_addr) { + *next_secondary_addr = '\0'; + ++next_secondary_addr; + } + + // Obtain the 32-bit, host-byte-order representation of + // the primary address. + struct in_addr foo; + int aton_retval = ACE_OS::inet_aton(ACE_TEXT_ALWAYS_CHAR (str), + &foo); + + // If this representation was not obtained, terminate with + // an error. + if (!aton_retval) { + + ACE_TCHAR error_message[Options_Manager::string_len + 100]; + ACE_OS::strcpy + (error_message, + ACE_TEXT ("Could not make sense of primary address: ")); + ACE_OS::strcat (error_message, str); + + _error = 1; + _error_message = ACE_OS::strdup(error_message); + break; + } + + // Otherwise, store the representation in the + // client_connect_addr member variable. + client_connect_addr = ntohl(foo.s_addr); + +// ACE_DEBUG ((LM_DEBUG, +// "Primary connect addr: %s retval = %d\n", +// str, +// aton_retval)); + +// if (next_secondary_addr) { +// ACE_DEBUG ((LM_DEBUG, +// "Secondary addr(s) remaining to be parsed: %s\n", +// next_secondary_addr)); +// } else { +// ACE_DEBUG ((LM_DEBUG, +// "No secondary addr remaining to be parsed.\n")); +// } + + // The following loop parses secondary addresses from the + // list. The loop repeats as long as ACE_OS::strchr + // returns non-null (i.e., as long as yet another comma is + // found. + while (next_secondary_addr && + num_secondary_connect_addrs < + max_num_secondary_connect_addrs) { + + // Get a pointer to the next comma in the list. + ACE_TCHAR *next_next_secondary_addr = ACE_OS::strchr(next_secondary_addr, ','); + + // If found, the comma is replaced with \0 and pointer + // updated to point to the string that begins immediately + // after the comma. + if (next_next_secondary_addr) { + *next_next_secondary_addr = '\0'; + ++next_next_secondary_addr; + } + + // Obtain the 32-bit, host-byte-order representation of + // a secondary address. + aton_retval = + ACE_OS::inet_aton(ACE_TEXT_ALWAYS_CHAR (next_secondary_addr), + &foo); + + // If the representation was obtained without error, + // store it in the next available slot of the + // secondary_connect_addrs array. Otherwise, terminate + // with an error. + if (aton_retval) { + secondary_connect_addrs[num_secondary_connect_addrs++] = + ntohl(foo.s_addr); + } else { + + ACE_TCHAR error_message[Options_Manager::string_len + 100]; + ACE_OS::strcpy + (error_message, + ACE_TEXT ("Could not make sense of secondary address: ")); + ACE_OS::strcat (error_message, next_secondary_addr); + _error = 1; + _error_message = ACE_OS::strdup(error_message); + break; + } + +// ACE_DEBUG ((LM_DEBUG, +// "secondary_addr[%d] = %s retval = %d\n", +// num_secondary_connect_addrs - 1, +// next_secondary_addr, +// aton_retval)); + + next_secondary_addr = next_next_secondary_addr; + } + + break; + } + case 'p': + server_port = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'a':{ + + // The argument to this option is a comma-separated list + // of dotted-decimal ipv4 addresses. + + // Create a writable copy of the options argument + ACE_TCHAR str[Options_Manager::string_len]; + ACE_OS::strncpy(str, get_opt->opt_arg(), Options_Manager::string_len); + + // Get a pointer to the first comma in the list + ACE_TCHAR *next_secondary_addr = ACE_OS::strchr(str, ','); + + // If found, the comma is replaced with \0 and pointer + // updated to point to the string that begins immediately + // after the comma. + if (next_secondary_addr) { + *next_secondary_addr = '\0'; + ++next_secondary_addr; + } + + // Obtain the 32-bit, host-byte-order representation of + // the primary address. + struct in_addr foo; + int aton_retval = ACE_OS::inet_aton (ACE_TEXT_ALWAYS_CHAR (str), + &foo); + + // If this representation was not obtained, terminate with + // an error. + if (!aton_retval) { + + ACE_TCHAR error_message[Options_Manager::string_len + 100]; + ACE_OS::strcpy + (error_message, + ACE_TEXT ("Could not make sense of primary address: ")); + ACE_OS::strcat (error_message, str); + + _error = 1; + _error_message = ACE_OS::strdup(error_message); + break; + } + + // Otherwise, store the representation in the + // server_accept_addr member variable. + server_accept_addr = ntohl(foo.s_addr); + +// ACE_DEBUG ((LM_DEBUG, +// "Primary accept addr: %s retval = %d\n", +// str, +// aton_retval)); + +// if (next_secondary_addr) { +// ACE_DEBUG ((LM_DEBUG, +// "Secondary addr(s) remaining to be parsed: %s\n", +// next_secondary_addr)); +// } else { +// ACE_DEBUG ((LM_DEBUG, +// "No secondary addr remaining to be parsed.\n")); +// } + + // The following loop parses secondary addresses from the + // list. The loop repeats as long as ACE_OS::strchr + // returns non-null (i.e., as long as yet another comma is + // found. + while (next_secondary_addr && + num_secondary_accept_addrs < + max_num_secondary_accept_addrs) { + + // Get a pointer to the next comma in the list. + ACE_TCHAR *next_next_secondary_addr = ACE_OS::strchr(next_secondary_addr, ','); + + // If found, the comma is replaced with \0 and pointer + // updated to point to the string that begins immediately + // after the comma. + if (next_next_secondary_addr) { + *next_next_secondary_addr = '\0'; + ++next_next_secondary_addr; + } + + // Obtain the 32-bit, host-byte-order representation of + // a secondary address. + aton_retval = + ACE_OS::inet_aton (ACE_TEXT_ALWAYS_CHAR (next_secondary_addr), + &foo); + + // If the representation was obtained without error, + // store it in the next available slot of the + // secondary_accept_addrs array. Otherwise, terminate + // with an error. + if (aton_retval) { + secondary_accept_addrs[num_secondary_accept_addrs++] = + ntohl(foo.s_addr); + } else { + + ACE_TCHAR error_message[Options_Manager::string_len + 100]; + ACE_OS::strcpy + (error_message, + ACE_TEXT ("Could not make sense of secondary address: ")); + ACE_OS::strcat (error_message, next_secondary_addr); + + _error = 1; + _error_message = ACE_OS::strdup(error_message); + break; + } + +// ACE_DEBUG ((LM_DEBUG, +// "secondary_addr[%d] = %s retval = %d\n", +// num_secondary_accept_addrs - 1, +// next_secondary_addr, +// aton_retval)); + + next_secondary_addr = next_next_secondary_addr; + } + + break; + } + case 'H':{ + ACE_TCHAR const * const str=get_opt->opt_arg(); + if (ACE_OS::strlen (str) < Options_Manager::string_len) + ACE_OS::strcpy(server_host, str); + else{ + ACE_OS::strncpy(server_host, str, (Options_Manager::string_len-1)); + server_host[Options_Manager::string_len - 1] = '\0'; + } + break; + } + case 's': + payload_size_power_of_2 = ACE_OS::atoi(get_opt->opt_arg ()); + break; + case 'h': + _usage = 1; + break; + } + } + + // Check option values + if (test_iterations < 1) + { + _error = 1; + _error_message = ACE_TEXT ("test_iterations must be no less than than 1"); + } + + if (histogram_min_bin < 0.0) + { + _error = 1; + _error_message = ACE_TEXT ("histogram_min_bin must be no less than 0.0"); + } + + if (histogram_max_bin < histogram_min_bin) + { + _error = 1; + _error_message = ACE_TEXT ("histogram_max_bin must be no less than histogram_min_bin"); + } + + if (histogram_num_outliers < 1) + { + _error = 1; + _error_message = ACE_TEXT ("histogram_num_outliers must be no less than 1"); + } + + if (histogram_bin_count < 1) + { + _error = 1; + _error_message = ACE_TEXT ("histogram_bin_count must be no less than 1"); + } + + if ((server_port < 1010 || + server_port > 65000) && server_port != 0) + { + _error = 1; + _error_message = ACE_TEXT ("server_port must be between 1010 and 65000 inclusive, or zero."); + } + + if ((!ACE_OS::strcmp(ACE_TEXT ("client-opts"), opts_set)) && payload_size_power_of_2 > 17) + { + _error = 1; + _error_message = ACE_TEXT("payload_size_power_of_2 must be specified between 0 and 16 inclusive"); + } + + if (test_transport_protocol == -1) { + _error = 1; + _error_message = ACE_TEXT ("test_transport_protocol may only take 'sctp' and 'tcp' as values"); + } + + __initialized = 1; + + delete get_opt; + } +} + +void Options_Manager::_show_usage(FILE* out, ACE_TCHAR const * const opts_set) +{ + // Show usage message. KEEP THE DEFAULTS DISPLAYED HERE IN SYNC + // WITH THE DEFAULTS SET AT THE BEGINNING OF THE CONSTRUCTOR. + + ACE_OS::fprintf (out, "%s - Measures round trip latency statistics of ACE synchronous\n", __program_name); + // indent past program name + for (unsigned int i=0;i<ACE_OS::strlen(__program_name);++i) + ACE_OS::fprintf (out, " "); + + if (ACE_OS::strstr(__program_name, ACE_TEXT ("SOCK_STREAM_clt")) || + ACE_OS::strstr(__program_name, ACE_TEXT ("SOCK_STREAM_srv")) ) { + ACE_OS::fprintf (out, " messaging (SOCK_Stream) using unmarshalled ACE_CDR::Octet.\n"); + } else { + ACE_OS::fprintf (out, " messaging (SOCK_SEQPACK) using unmarshalled ACE_CDR::Octet.\n"); + } + + ACE_OS::fprintf (out, "USAGE: %s [ -<flag> [<val>] | --<name> [<val>] ]...\n\n", __program_name); + + if (!ACE_OS::strcmp (ACE_TEXT ("client-opts"), opts_set)){ + ACE_OS::fprintf (out, " Flag Args Option-Name Default\n" + " -c int test-iterations 1000000\n" + " -n none test-enable-nagle NO NAGLING\n"); + if (ACE_OS::strstr(__program_name, ACE_TEXT ("SOCK_STREAM_clt"))) { + ACE_OS::fprintf (out, " -t str (sctp|tcp) test-transport-protocol sctp\n"); + } + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -m dbl histogram-min-bin 0\n" + " -M dbl histogram-max-bin 10000\n" + " -x int histogram-num-outliers 100\n" + " -b int histogram-bin-count 1000\n"); + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -C int client-port assigned by kernel\n" + " -i str client-connect-addr INADDR_ANY\n"); + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -p int server-port 45453\n" + " -H str server-host localhost\n"); + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -s int (0-16) payload-size-power-of-2 <MUST SET VALUE>\n"); + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -h none help\n"); + + ACE_OS::fprintf (out, "\n"); + + } else if (!ACE_OS::strcmp(ACE_TEXT ("server-opts"), opts_set)){ + ACE_OS::fprintf (out, " Flag Args Option-Name Default\n" + " -n none test-enable-nagle NO NAGLING\n"); + if (ACE_OS::strstr(__program_name, ACE_TEXT ("SOCK_STREAM_srv"))) { + ACE_OS::fprintf (out, " -t str (sctp|tcp) test-transport-protocol sctp\n"); + } + + ACE_OS::fprintf (out, "\n"); + + ACE_OS::fprintf (out, " -p int server-port 45453\n"); + + if (ACE_OS::strstr(__program_name, ACE_TEXT ("SOCK_SEQPACK_srv"))) { + ACE_OS::fprintf (out, " -a w.x.y.z,a.b.c.d,... server-accept-addr INADDR_ANY\n" + " (comma-separated \n" + " list of one or more \n" + " addresses) \n"); + } else { + ACE_OS::fprintf (out, " -a w.x.y.z server-accept-addr INADDR_ANY\n"); + } + + ACE_OS::fprintf (out, " -h none help\n"); + + ACE_OS::fprintf (out, "\n"); + + } else { + ACE_OS::fprintf (out, "Invalid options set specified.\n"); + } + + return; +} diff --git a/ACE/performance-tests/SCTP/Options_Manager.h b/ACE/performance-tests/SCTP/Options_Manager.h new file mode 100644 index 00000000000..7fbcc2dbfcc --- /dev/null +++ b/ACE/performance-tests/SCTP/Options_Manager.h @@ -0,0 +1,67 @@ +// -*- C++ -*- + +// $Id$ + +// The types defined in ACE_CDR are more complete that those in Basic_Types.h + + +#ifndef OPTIONS_MANAGER_H +#define OPTIONS_MANAGER_H + +#ifndef ACE_CDR_BASE_H +#include "ace/CDR_Base.h" +#endif + +#include "ace/streams.h" +#include "ace/OS_NS_stdio.h" + +class Options_Manager +{ +public: + + Options_Manager(int argc, ACE_TCHAR **argv, ACE_TCHAR const * const opts_set); + + // constant string size + enum { string_len = 100 }; + + static ACE_CDR::ULong test_iterations; + static ACE_CDR::Boolean test_enable_nagle; + static ACE_CDR::Long test_transport_protocol; + + static ACE_CDR::Double histogram_min_bin; + static ACE_CDR::Double histogram_max_bin; + static ACE_CDR::ULong histogram_bin_count; + static ACE_CDR::ULong histogram_num_outliers; + + static ACE_CDR::UShort client_port; + static ACE_CDR::ULong client_connect_addr; + + static ACE_CDR::UShort server_port; + static ACE_TCHAR server_host[string_len]; + static ACE_CDR::ULong server_accept_addr; + + static ACE_CDR::UShort payload_size_power_of_2; + + // Secondary client_connect_addrs for SCTP SOCK_SEQPACK test + enum { max_num_secondary_connect_addrs = 100 }; + static ACE_CDR::ULong secondary_connect_addrs[max_num_secondary_connect_addrs]; + static ACE_CDR::UShort num_secondary_connect_addrs; + + // Secondary server_accept_addrs for SCTP SOCK_SEQPACK test + enum { max_num_secondary_accept_addrs = 100 }; + static ACE_CDR::ULong secondary_accept_addrs[max_num_secondary_accept_addrs]; + static ACE_CDR::UShort num_secondary_accept_addrs; + + static ACE_CDR::UShort _error; + static const ACE_TCHAR* _error_message; + + static ACE_CDR::UShort _usage; + void _show_usage(FILE* out, ACE_TCHAR const * const opts_set); + +private: + static ACE_CDR::Boolean __initialized; + static ACE_TCHAR __program_name[string_len]; + +}; + +#endif /* OPTIONS_MANAGER_H */ diff --git a/ACE/performance-tests/SCTP/README b/ACE/performance-tests/SCTP/README new file mode 100644 index 00000000000..f6d0ac65954 --- /dev/null +++ b/ACE/performance-tests/SCTP/README @@ -0,0 +1,81 @@ +This directory provides programs that measure the round trip latency +of synchronous octet messaging using ACE wrapper-facades for the SCTP +protocol. Currently these programs provide the only example code on +how to use ACE's wrapper-facades for SCTP. In the future additional +code will be placed in the ACE_wrappers/examples/IPC_SAP/SOCK_SAP +directory. + +SOCK_STREAM_clt and SOCK_STREAM_srv use the SOCK_STREAM service +provided by SCTP via ACE's SOCK_Connector, SOCK_Stream, +SOCK_Association wrapper-facade classes. + +SOCK_SEQPACK_clt and SOCK_SEQPACK_srv use the SOCK_SEQPACKET service +provided by SCTP via ACE's SOCK_SEQPACK_Connector, +SOCK_SEQPACK_Association and SOCK_SEQPACK_Acceptor wrapper-facade +classes. + +The following table provides a summary of relationships between two main +Linux SCTP implementations (OpenSS7 and LKSCTP), and ACE Wrapper Facades +support that is provided. Built on top of the ACE Wrapper Facade support is +the SCIOP support in TAO. SCIOP works identically on both OpenSS7 and +LKSCTP implementations. Please read additional README* files listed below. + +------------------------------------------------------------------------------- + SCTP Transport Transport ACE + Protocol Service Service Wrapper + Impl. Type Characteristics Facades + ------------------------------------------------------------------------------ + OpenSS7 SOCK_SEQPACKET {Connected, ACE_SOCK_SEQPACKET_Acceptor + Reliable, ACE_SOCK_SEQPACKET_Connector + Messages} ACE_SOCK_SEQPACKET_Association + + OpenSS7 SOCK_STREAM {Connected, ACE_SOCK_Acceptor(IPPROTO_SCTP) + Reliable, ACE_SOCK_Connector(IPPROTO_SCTP) + Byte Stream} ACE_SOCK_Stream(IPPROTO_SCTP) + + OpenSS7 SOCK_RDM {Connectionless, NOT IMPLEMENTED IN ACE + Reliable, + Messages} + + + LKSCTP SOCK_SEQPACKET {Connectionless, NOT USED IN ACE + UNRELIABLE, + Messages} + + LKSCTP SOCK_STREAM {Connected, ACE_SOCK_SEQPACKET_Acceptor + Reliable, ACE_SOCK_SEQPACKET_Connector + Messages} ACE_SOCK_SEQPACKET_Association + ACE_SOCK_Acceptor(IPPROTO_SCTP) + ACE_SOCK_Connector(IPPROTO_SCTP) + ACE_SOCK_Stream(IPPROTO_SCTP) + + +Additional information can be found in the following README files also +located in this directory. + + README.SCTP - Brief description of the major features and benefits + SCTP protocol and references for further information. + + README.SCTP_in_ACE - Description of the wrapper-facade classes that + ACE provides to use SCTP, general usage + guidance, release notes, technical point of + contact information. + + README.SCTP_PERF_TEST - Detailed description on how to use the + performance tests and the run_spectrum.pl + script contained in this directory. + Discussion of the baseline performance + results obtained by LM ATL. + + THANKS - Documents all the people who contributed to the development + of the ACE wrapper-facades for SCTP and the SCIOP (SCTP + Inter-ORB Protocol) for TAO. Information on that can be + found in ACE_wrappers/TAO/tao/Strategies/README.SCIOP + +General requests for information beyond what is contained in these +files can be sent to either of the following individuals. + +Patrick Lardieri plardier@atl.lmco.com +Gautam Thaker gthaker@atl.lmco.com + +This work was sponsored by DARPA/IXO PCES Program. diff --git a/ACE/performance-tests/SCTP/README.LKSCTP b/ACE/performance-tests/SCTP/README.LKSCTP new file mode 100644 index 00000000000..494167abe30 --- /dev/null +++ b/ACE/performance-tests/SCTP/README.LKSCTP @@ -0,0 +1,46 @@ +This README describes the LKSCTP implementation of SCTP for Linux. + +June 2003 - 1.0 + +Overview +-------- + +The LKSCTP project is the Linux Kernel implementation of SCTP. As of this +writing it has been incorporated into the Linux 2.6 test series of +kernels. + +LKSCTP adheres to the IETF's SCTP Sockets Draft API. Please refer to that +document for API specifics. + +You MUST use Linux 2.6-test4 or later. Earlier versions have a different +api. + +Building ACE/TAO with LKSCTP support +-------------------------------- + + - compile kernel with SCTP support + + if compiled as a module, be sure to load it. + + - download LKSCTP userspace functions library from + http://lksctp.sourceforge.net/ + + - install userspace library (libsctp.a) into /usr/local/lib + + - install header file (sctp.h) into /usr/local/include/sctp.h + + - make ACE/TAO with "sctp=lksctp" + +Caveats +--------------------------------- + + - Both services interfaces (SOCK_STREAM and SOCK_SEQPACKET) will + preserve message boundaries. + +Resources +--------------------------------- + + - LKSCTP Homepage: http://lksctp.sourceforge.net/ + - Latest Patches + - Userspace Library + - Mailing Lists +-- diff --git a/ACE/performance-tests/SCTP/README.OpenSS7 b/ACE/performance-tests/SCTP/README.OpenSS7 new file mode 100644 index 00000000000..5183e872e12 --- /dev/null +++ b/ACE/performance-tests/SCTP/README.OpenSS7 @@ -0,0 +1,271 @@ +// $Id$ + +This document describes the OpenSS7 API in detail. + +Introduction +============= + +OpenSS7 defines two types of services, UDP style and TCP style. How OpenSS7 +distinguishes the two styles is by the way in which the data is presented to +the application. In OpenSS7, data is presented as byte streams or datagrams. +The stream style is considered the TCP style socket, and the datagram is +considered the UDP style socket. The UDP style socket does not represent a one +to many mapping of file descriptors to associations as the IETF compatible +sockets does. + +Instead of adding new system calls, OpenSS7 decided to modify some of the +existing system calls to play nicely with multi-homing. So, to bind to +multiple addresses, you pass in an array of struct sockaddr* structures and the +total length in bytes of the array. Also, when accepting connections from a +peer, you would pass in a pointer to an array of struct sockaddr* and the +length of the array in bytes. + + +Building ACE/TAO with support for OpenSS7 SCTP +============================================== + + - apply OpenSS7's SCTP patch (see OpenSS7's readme for details) + + - compile kernel with the following kernel variables (.config file) + + CONFIG_SCTP=y + + CONFIG_SCTP_TCP_COMPATIBLE=y + + CONFIG_SCTP_HMAC_SHA1=y + + CONFIG_SCTP_HMAC_MD5=y + + CONFIG_SCTP_ADLER_32=y + + CONFIG_SCTP_CRC_32C=y + + - install header file (sctp.h) into /usr/local/include/sctp.h + + - make ACE/TAO with "sctp=openss7" -- ie. "make sctp=openss7" + +Common Calls +============ + +There are a few common calls which have the same semantics no matter which +interface you are using (UDP style or TCP style). So, the usual calls used in +a client are: + + 1) socket() + 2) bind() + 3) setsockopt() + 4) connect() + 5) send/recv() + 6) close() + +while the usual calls for a server are as follows: + + 1) socket() + 2) bind() + 3) setsockopt() + 4) listen() + 5) accept() + 6) close() + +The previous example mimics the way in which the TCP protocol is used. +There are some exceptions to the above sequence which we will get into a little +later. So, let's talk about the system calls above. + +Socket() +-------- +The socket() call creates a file descriptor for the application to use to +communicate with the SCTP stack. The signature of socket() is as follows: + + int socket(int network, int service, int proto) + Where: + network - AF_INET or AF_INET6 (AF_INET6 is not currently supported) + service - SOCK_SEQPACKET which gives us the UDP style interface + SOCK_STREAM which gives us the TCP style interface + proto - IPPROTO_SCTP which is the SCTP protocol number(132) + +Bind() +------ +The bind() call is used to attach the socket descriptor to the given array of +addresses. bind() may only be called once. The address structures sent in as +arguments to bind must have the same port number. That is, you may have +multiple addresses, but only 1 port number. The signature of bind() is as +follows: + + int bind(int fd, struct sockaddr* addrs, socklen_t addrs_len) + Where: + fd - A SCTP socket descriptor + addrs - An array of struct sockaddr* which have the same port number + addrs_len - The total length in bytes of the addrs array + +If an address structure is sent in with the address of INADDR_ANY, then SCTP +will bind to all the addresses on the host and either pick an ephemeral port or +use the one specified in the address structure. + +Setsockopt() +------------ +The setsockopt() call is used to adjust many different parameters to the SCTP +protocol. Please see the sctp(7) man page for more details. + +Connect() +--------- +The connect() call can be used to initialize a connection with a peer SCTP +machine. A connection can be initialized in 2 ways. The first is this +call(connect()). The second way is to allow implicit initialization when the +application calls sendmsg or sendto with the address of the remote host +specified. To allow data to be piggybacked with the COOKIE_ECHO chunk, you +must use the second initialization technique. The signature of connect is as +follows: + + int connect(int fd, struct sockaddr* r_addr, socklen_t r_addr_len) + Where: + fd - An SCTP socket descriptor + r_addr - A single address structure with the address of the remote host + r_addr_len - Length in bytes of the the address structure + +Note that only one address is sent into the connect call. The SCTP protocol +will negotiate the valid IP addresses to use with multi-homing. + +Listen() +-------- +The listen() call prepares the SCTP socket to accept new associations. The +signature is as follows: + + int listen(int fd, int backlog) + Where: + fd - An SCTP socket descriptor + backlog - The maximum number of unaccepted connections + +Accept() +-------- +The accept() call allows the application to explicitly accept an incoming +association initialization attempt. accept() returns a new file descriptor +which can be used to send data to the client. accept() has a slightly +different semantic which allows it to take an array of address structures. +This gives the application the chance to get a specified number of addresses +from the peer on initialization. The signature of accept() is as follows: + + int accept(int fd, struct sockaddr* r_addr, socklen_t *r_addr_len) + Where: + fd - A SCTP socket descriptor + r_addr - An array of address structures which will be filled with the + addresses of the remote host + r_addr_len - The length of the array of address structures passed in. + +accept() will only put as many addresses in the address array as is specified +by the r_addr_len argument. + +Close() +------- +Start the association shutdown sequence. The signature is as follows: + + int close(int fd) + Where: + fd - A SCTP socket descriptor + +Data Interfaces +=============== +This section describes the sending and receiving of data on an SCTP socket. +This is where the differences between the TCP style and UDP style sockets +shows. SCTP uses the standard pairs of calls: + + 1) send()/recv() + 2) sendmsg()/recvmsg() + 3) sendto()/readfrom() + 4) write()/read() + 5) writev()/readv() + +When the SCTP socket is opened as a UDP style socket, data is presented to the +application as datagrams. This means that calls to the receive class of system +calls returns only datagrams. For example, assume a sender sends 2 100 byte +datagrams. Now assume that the receiver calls one of the receive routines with +a max buffer size of 120 bytes. Assuming no errors are encountered, only 100 +bytes are returned to the receiver (only 1 datagram), leaving the last 20 bytes +empty in the receivers buffer. When the last of a datagram is read, the +MSG_EOR flag is returned, and if the datagram is too big to fit into the +specified buffer, the MSG_TRUNC flag is returned. + +With TCP styles sockets, the data is presented to the application as a stream +of bytes (like TCP). For example, assume the sender sends 2 100 byte +datagrams. Now assume that the receiver calls one of the receive routines with +a max buffer size of 120. Assuming no errors are encountered, 120 bytes are +returned (100 from the first datagram and 20 from the next datagram), leaving +80 bytes in the second datagram. MSG_EOR and MSG_TRUNC are never returned. + +Send()/Recv() +------------- +The send/recv interface is one of the easiest ways of sending data back and +forth between peers. When send/recv is called, data is sent/read from the +default stream. The signature of send/recv is as follows: + + int send(int fd, void *data, size_t d_len, int flags) + Where: + fd - A SCTP socket descriptor + data - Data to be sent to the peer + d_len - The length of the data in bytes + flags - Possible flags to be sent to be passed to the SCTP stack + (see send(3)) + +Sendmsg()/Recvmsg() +------------------- +The sendmsg/recvmsg interface is the most powerful and hard to use interface +in the SCTP stack. This interface allows the specification of which stream to +send data out or receive data from. It also allows the sending of data with a +specified protocol payload identifier. The signature is as follows: + + int sendmsg(int fd, struct msghdr *msg, int flags) + Where: + fd - A SCTP socket descriptor + msg - A struct msghdr structure which holds miscellaneous information + flags - Possible flags to be passed to the SCTP stack + (see sendmsg(3)) + +The msg argument holds the data to be sent to the peer. It also can take an +address to send the data to. So, for example, if you had an association with 2 +addresses for the peer and the default path used the first address, and you +wanted to send data to the other address, you could specify that address in the +msg structure. If this address field is NULL, then data is sent out the +default address. + +This interface also allows other functions by using the ancillary data field +in the msg structure. By using the ancillary data field, you may send data out +a different stream by using the SCTP_SID control message. This type of control +message would take an integer representing the stream to send data out as it's +data. Another option is to use the SCTP_PPI control message which allows the +setting of the protocol payload identifier to the specified integer argument. +Both of these options only set their values for the current data being sent out. + +Sendto()/Recvfrom() +------------------- +The sendto/recvfrom interface allows the specification of which remote address +to send data. The signature of sendto/recvfrom is as follows: + + int sendto(int fd, void *data, size_t d_len, int flags, + struct sockaddr* r_addr, socklen_t r_addr_len) + Where: + fd - A SCTP socket descriptor + data - Data to send to peer + d_len - Length of the data to be sent + flags - Possible flags to be passed to the SCTP stack + r_addr - Address of peer to send data to + r_addr_len - Length of peer address + +Write()/Read() +-------------- +This interface is the simplest way of sending data to a peer. When this +interface is used, data is sent/received through the default stream (as set by +SCTP_SID). The signature is as follows: + + int write(int fd, void *data, size_t d_len) + Where: + fd - A SCTP socket descriptor + data - Data to be sent to peer + d_len - Length of data + +Writev()/Readv() +---------------- +This interface allows the sending of multiple buffers of data in one system +call. This call places all the buffers into one SCTP data chunk. The signature is as follows: + + int writev(int fd, struct iovec *iov, int ct) + Where: + fd - A SCTP socket descriptor + iov - An array of iovec structures. The order they are in the array is the + order they are placed in the data chunk + ct - The number of iovec structures to send + +-- + diff --git a/ACE/performance-tests/SCTP/README.SCIOP b/ACE/performance-tests/SCTP/README.SCIOP new file mode 100644 index 00000000000..503b81f7eaf --- /dev/null +++ b/ACE/performance-tests/SCTP/README.SCIOP @@ -0,0 +1,63 @@ +README.SCIOP +------------ + +Author: Jason Cohen +Lockheed Martin, Advanced Technology Labs + + +The SCIOP pluggable protocol for TAO uses the SCTP transport protocol through +the use of the SOCK_SEQPACK ACE wrappers for sequence based transport. The +current implementation supported is the OpenSS7 SCTP protocol layer, tested +under Linux 2.4.18. + +For more information on SOCK_SEQPACK and OpenSS7 SCTP see +ACE_wrappers/performance-tests/SCTP/README* + +Features Currently Supported: +----------------------------- + +o) Sequenced delivery via SOCK_SEQPACK +Currently, only one stream is supported as there are no available bindings to +provide a user with stream designation for messages. + +o) SCIOR compliance to OMG GIOP SCTP Protocol Mapping submission mars/2002-09-01. + +o) Multiple address SCIOR profiles +When multiple addresses are included in a SCIOR profile, each address is +handled separately during the connection process. Specifying multiple +addresses for a single connection attempt remains as work to be done. +However, multiple path discovery during connection time in OpenSS7's +SCTP implementation has been tested to work properly with SCIOP. + +The catior utility under ACE_wrappers/TAO/utils/catior has also modified +to display SCIOR profile information. + +To test SCIOP after installing an SCTP implementation supported by +SOCK_SEQPACK, try using a performance test specifying an sciop address as the +ORBEndpoint. One way to do this is with the test found under: +ACE_wrappers/TAO/performance-tests/Pluggable/ + +Make sure to build the pluggable protocols into the executable: +gmake LIBS+=-lTAO_Strategies + +Run the server with an sciop endpoint specified: +./server -ORBEndpoint sciop://${HOSTNAME} -o test.ior & + +Finally, run the client on the same machine or another that has file +access to your IOR file: +./client -f test.ior + +TO-DO: +------ + +o) Multiple association connection using Multihomed_INET_Addr +Currently, only one address connection is attempted at a time, even when a +profile contains multiple address. Later, we hope to allow this +implementation of SCIOP to exploit the multiple address connection +capabilities of STCP. + +o) SCTP ProtocolManagement/SCTPProtocolProperties + +o) Support for max_streams > 1 + +o) SCIOR Profile Components diff --git a/ACE/performance-tests/SCTP/README.SCTP b/ACE/performance-tests/SCTP/README.SCTP new file mode 100644 index 00000000000..95c3cb0b53a --- /dev/null +++ b/ACE/performance-tests/SCTP/README.SCTP @@ -0,0 +1,81 @@ +# $Id$ + +This document provides general information about the SCTP protocol. + + +Introduction +============= +SCTP (Stream Control Transmission Protocol) is a new transport layer protocol +developed by the IETF and defined by RFC2960. Originally designed for the +transmission of message-oriented applications such as the transportation of +signaling data for PSTNs (Public Switched Telephone Networks), SCTP can be used +in any application that uses TCP. + + Major Features + -------------- + + - Direct support for multi-homing + - Support for multiple streams per connection (known as associations) + to reduce "head of line" blocking problems. + - Four way handshake reducing the possibility for blind denial of + service attacks. + - Adjustment of congestion control parameters + - Cookie mechanism to protect against "SYN" attacks + +API's +======== + +The SCTP drafts have defined several different API's. The first reference +implementation was a user level library. This allowed an application to use +SCTP through several functions and callbacks. As of this writing a +transition from the user level libraries to the kernel is underway. + +The IETF TSVWG (Transport Area Working Group) has published a draft +(draft-ietf-tsvwg-sctpsocket-06.txt) that defines a mapping between SCTP and +the BSD sockets interface. This interface has made porting TCP applications to +SCTP as simple as changing one socket call. + + OpenSS7 + -------- + OpenSS7 has defined, a similar BSD style sockets interface. It is + slightly different from the one defined by the IETF, however, at the + time of our evaluation it was the most stable implementation. + + Please see README.OpenSS7 for details and build instructions. + + LKSCTP + ------ + The LKSCTP implementation is based on the TSVWG's sockets draft. + It is included with the Linux 2.6 series test kernels. Backported + (Linux 2.4) patches are also available from the LKSCTP website. + + Please see README.LKSCTP for more details and build instructions. + +Resources +=========== + + RFCs + ----- + RFC2960: Stream Control Transmission Protocol + RFC3309: Stream Control Transmission Protocol Checksum Change + + Internet-Drafts + ---------------- + + draft-ietf-tsvwg-sctpsocket-07.txt: + Sockets API Extensions for Stream Control Transmission Protocol + + draft-ietf-tsvwg-sctpimpguide-08.txt + Stream Control Transmission Protocol Implementer's Guide + + Internet Links + --------------- + IETF: Transport Area Working Group (tsvwg): + http://www.ietf.org/html.charters/tsvwg-charter.html + + The OpenSS7 Project: http://www.openss7.org/sctp.html + + The LKSCTP Project: http://lksctp.sourceforge.net/ + +-- + diff --git a/ACE/performance-tests/SCTP/README.SCTP_PERF_TEST b/ACE/performance-tests/SCTP/README.SCTP_PERF_TEST new file mode 100644 index 00000000000..81cacef02e0 --- /dev/null +++ b/ACE/performance-tests/SCTP/README.SCTP_PERF_TEST @@ -0,0 +1,405 @@ +$Id$ + +Release 1.0 - January 2003 + +*********************************** +* Performance Test Overview * +*********************************** + +This directory contains performance tests for measuring round-trip +latency statistics of ACE synchronous messaging using unmarshalled +ACE_CDR::Octet. + +In terms of protocol and service type, three different tests are +available: + + * TCP over SOCK_STREAM + * SCTP over SOCK_STREAM + * SCTP over SOCK_SEQPACK + +All three of these tests share the same architecture, which is +described in the following section. + +*************************** +* Test Architecture * +*************************** + +As mentioned above, the performance tests measure round-trip latency +statistics. We are talking about the round trip between two service +endpoints, a client and a server. The client sends the server an +arbitrary message with a specific length---up to 2^16 bytes---and the +server responds with a 2-byte reply. The client measures and records +the time it takes to complete this exchange. The whole test is then +repeated through many iterations, and finally the client outputs the +resulting statistics. + +In detail, + +1. The server program is started first. It creates a passive-mode + socket and runs an infinite loop waiting for clients to connect. + +2. The client program is started next. It may be configured with + numerous parameters, which include: + + * the hostname and port where the server's passive-mode socket + is listening, + * the desired message length, which may be any power of two + between 2^0 and 2^16, + * the desired number of iterations (by default 1 million) + +3. The client connects to the server's passive-mode socket. If all + goes well, the client and server will each obtain a data-mode + socket that serves as a communication endpoint. + +4. The client sends an initial header message containing the intended + number of iterations and the message length (which will be + the same for each iteration). Armed with this information, the + server spawns a separate thread to handle these iterations. + +5. The client creates an in-memory data structure that serves as a + histogram. The histogram is configured with a range of possible + round-trip latencies, and this range is divided into a number of + "bins". The latency measured for each test entry will either + fall into one of these "bins", or it will fall outside the entire + range and will be logged as an outlier. + +6. The client and server repeat the following interaction for each + test iteration. + + a. The client starts a stopwatch. + + b. The client sends the server an arbitrary message of the + established length. + + c. After receiving this entire message from the client, the + server sends the client a 2-byte reply. + + d. After receiving the 2-byte reply from the server, the client + stops the stopwatch. + + e. The round-trip latency, as measureed by the stopwatch, is + logged to the in-memory histogram. + +7. After all these iterations are complete, the client disconnects + from the server. The client then takes the in-memory histogram + and dumps its text representation to STDOUT. + +8. The client exits. The server thread dedicated to this client + exits. The main thread of the server continues to run an infinite + loop waiting for more clients to connect to the passive-mode + socket. + +************************** +* Test Executables * +************************** + +You must use a different pair of client and server executables +depending on whether you desire to use the SOCK_STREAM service or the +SOCK_SEQPACK service. + +The SOCK_STREAM client and server are called SOCK_STREAM_clt and +SOCK_STREAM_srv, respectively. The SOCK_SEQPACK client and server are +called SOCK_SEQPACK_clt and SOCK_SEQPACK_srv, respectively. + +Each of these executables uses SCTP as its default protocol. As an +extra feature, SOCK_STREAM_clt and SOCK_STREAM_srv offer TCP as an +alternate protocol. The choice between TCP and SCTP is exposed via +the '-t' option. (Individual instances of SOCK_STREAM_clt and +SOCK_STREAM_srv must use the same protocol in order to connect and +function.) + +Any of the four executables will show usage information if you run it +with the '-h' option. For instance, here is the usage message +produced by SOCK_STREAM_clt: + +./SOCK_STREAM_clt - Measures round trip latency statistics of ACE synchronous + messaging (SOCK_Stream) using unmarshalled ACE_CDR::Octet. +USAGE: ./SOCK_STREAM_clt [ -<flag> [<val>] | --<name> [<val>] ]... + + Flag Args Option-Name Default + -c int test-iterations 1000000 + -n none test-enable-nagle NO NAGLING + -t str (sctp|tcp) test-transport-protocol sctp + + -m dbl histogram-min-bin 0 + -M dbl histogram-max-bin 10000 + -x int histogram-num-outliers 100 + -b int histogram-bin-count 1000 + + -C int client-port assigned by kernel + -i str client-connect-addr INADDR_ANY + + -p int server-port 45453 + -H str server-host localhost + + -s int (0-16) payload-size-power-of-2 <MUST SET VALUE> + + -h none help + +For each option, the long "Option-Name" may be prefixed by two dashes +and used on the command line in place of the shorter "Flag". Hence + + ./SOCK_STREAM_clt -s 5 + +is equivalent to + + ./SOCK_STREAM_clt --payload-size-power-of-2 5 + +The options shown above for SOCK_STREAM_clt (and the similar options +available for SOCK_SEQPACK_clt) are discussed in the next section. + +***************************** +* Client-Side Options * +***************************** + +The following options are available for both SOCK_STREAM_clt and +SOCK_SEQPACK_clt. + + test-iterations [ -c int ] + + the number of times to repeat the round-trip-latency test + loop. The default is 1000000. + + test-enable-nagle [ -n ] + + Enable Nagle's algorithm (which is disabled by default). + + histogram-min-bin [ -m double ] + + the lower boundary on the range of the histogram. + The default is 0. The unit of measurement is the millisecond. + + histogram-max-bin [ -M double ] + + the upper boundary on the range of the histogram. + The default is 10000. The unit of measurement is the + millisecond. + + histogram-num-outliers [ -x int ] + + the maximum number of outliers to maintain in the histogram. + The default is 100. + + histogram-bin-count [ -b int ] + + the number of histogram bins. The default is 1000. The width + of each bin will be + + (histogram-max-bin - histogram-min-bin) / histogram-bin-count + + client-port [ -C int ] + client-connect-addr [ -i string ] + + the port and network address where the client's data-mode + socket is to be bound. By default, the port number is + assigned by the kernel, and the socket is bound to all network + interfaces. + + server-port [ -p int ] + server-host [ -H string ] + + the port and hostname where the server's passive-mode socket + is bound. The default port is 45453 and the default host is + localhost. + + payload-size-power-of-2 [ -s int ] + + an integer X. The size of the payload will be 2^X bytes. + + help [ -h ] + + Show usage message. + +The following option is available only for SOCK_STREAM_clt: + + test-transport-protocol [ -t (sctp|tcp) ] + + the protocol for the test. The default is sctp. + +In contrast, SOCK_SEQPACK_clt always uses SCTP as its protocol. + +***************************** +* Server-Side Options * +***************************** + +The following options are available for both SOCK_STREAM_srv and +SOCK_SEQPACK_srv: + + test-enable-nagle [ -n ] + + Enable Nagle's algorithm (which is disabled by default). + + server-port [ -p int ] + + the port where the server's passive-mode socket will be + bound. The default is 45453. + + help [ -h ] + + Show usage message. + +The following option is available for both SOCK_STREAM_srv and +SOCK_SEQPACK_srv, but the latter offers additional functionality: + + server-accept-addr [ -a w.x.y.z ] (for SOCK_STREAM_srv) + [ -a w.x.y.z,a.b.c.d,... ] (for SOCK_SEQPACK_srv) + + the network address (or addresses) to which the server's + passive-mode socket will be bound. + + The default value of INADDR_ANY should be sufficient for + machines that have only one network interface. + + If your machine has two interfaces, and you wish to bind the + socket only to one of these, then you may explicitly specify + the desired interface with an expression such as + "-a 192.168.221.104" or "-a 192.168.1.43". + + If your machine has three or more interfaces, and you wish to + bind the socket only to a subset of two or more, AND you are + using SOCK_SEQPACK_srv, THEN you may explicitly specify the + desired interface with an expression such as + "-a 192.168.221.104,192.168.1.43". The argument here is a + comma-delimited list of dotted-decimal IPv4 addresses with no + whitespace interspersed. This level of control is not + possible with SOCK_STREAM_srv. + +The following option is available only for SOCK_STREAM_srv: + + test-transport-protocol [ -t (sctp|tcp) ] + + the protocol for the test. The default is sctp. + +In contrast, SOCK_SEQPACK_srv always uses SCTP as its protocol. + +*************************** +* Test Walk-Through * +*************************** + +This section shows a walk-through of a typical scenario using +SOCK_STREAM_clt and SOCK_STREAM_srv to run an SCTP performance test. + +You can run the equivalent TCP performance test by adding "-t tcp" to +the command line of both programs. Alternately, you can run a +SOCK_SEQPACK-based SCTP performance test by substituting +SOCK_SEQPACK_clt and SOCK_SEQPACK_srv for SOCK_STREAM_clt and +SOCK_STREAM_srv, respectively. + +In this scenario, both client and server run on the same machine. We +use a message size of 2^7 bytes and a relatively small number of +iterations, 1000, in order to make the test go quickly. The server's +passive-mode socket binds to the default port, 45453, which is also +the default port expected by the client. We invoke no other special +or unusual options. + +For clarity, we envision starting the client and server in two +separate shells. + +The steps of the walk-through are as follows: + +1. Start the server. + + $ ./SOCK_STREAM_srv + (12761|1024) Accepting connections on port 45453 + on interface INADDR_ANY using IPPROTO_SCTP + (12761|1024) starting server at port 45453 + (12761|1024) select timed out + (12761|1024) select timed out + (12761|1024) select timed out + (12761|1024) select timed out + ... + + The server will continue to print, periodically, the message + "select timed out" as it waits for clients to connect. + +2. Start the client. + + $ ./SOCK_STREAM_clt -s 7 -c 1000 + +3. Observing the debugging output from the server, you should see + evidence that the client has connected. + + (12761|1024) select timed out + (12761|1024) select timed out + (12761|1024) select timed out + (12761|1024) spawning server + (12761|1024) client utica-b connected from 32768 + (12761|1024) Test for 1100 iterations + (12761|1024) select timed out + (12761|1024) select timed out + + Note that the server expects 1100 iterations, even though we + configured the client for only 1000 iterations. The reason for + this discrepencany is that the client always runs 100 primer + iterations before the requested test iterations. + +4. When the 1100 iterations are complete, observing the output from + the client, you should see the histogram: + + + Histogram ACE Unmarshalled Msg Synchronous Latency Test + (Message Size 128, Message Type octet) + version: 1.1 + minimum: 41 + maximum: 60182 + mean: 106.858 + variance: 3.62659e+06 + num_points: 1000 + num_bins: 1000 0 10000 + + Low - High Count Fraction Cumulative + below - 0.000 : 0 0.000 0.000 + 0.000 - 10.000 : 0 0.000 0.000 + 10.000 - 20.000 : 0 0.000 0.000 + 20.000 - 30.000 : 0 0.000 0.000 + 30.000 - 40.000 : 0 0.000 0.000 + 40.000 - 50.000 : 988 0.988 0.988 + 50.000 - 60.000 : 5 0.005 0.993 + 60.000 - 70.000 : 2 0.002 0.995 + 70.000 - 80.000 : 0 0.000 0.995 + 80.000 - 90.000 : 1 0.001 0.996 + 90.000 - 100.000 : 0 0.000 0.996 + 100.000 - 110.000 : 0 0.000 0.996 + ... + 9960.000 - 9970.000 : 0 0.000 0.999 + 9970.000 - 9980.000 : 0 0.000 0.999 + 9980.000 - 9990.000 : 0 0.000 0.999 + 9990.000 - 10000.000 : 0 0.000 0.999 + 10000.000 - above : 1 0.001 1.000 + + outliers: + 60182.000 + +****************************************** +* Running a Complete Test Spectrum * +****************************************** + +People who are interested in round-trip latency often want to see a +"spectrum" of statistics for a range of payload sizes. This directory +includes a script, run_spectrum.pl, that automates running +SOCK_STREAM_clt or SOCK_SEQPACK_clt multiple times in order to +generate a spectrum. (The appropriate server program must be started +manually.) + +The run_spectrum.pl script offers embedded documentation. To see the +full documentation, please run + + ./run_spectrum.pl --manual + +************************ +* Sample Results * +************************ + +The file sample-spectrum.png plots the spectrum data for a test run at +LM ATL facilities on the following systems: two Linux 2.4.18, 1600 MHz +Pentium 4 machines on an isolated 100Mbps Ethernet network. + +The test conditions include several critical parameters that were set +as follows: + + sctp_rto_initial = 20 + sctp_rto_min = 20 + sctp_rto_max = 20 + +Testing has shown that these parameters affect maximum round-trip +latency. diff --git a/ACE/performance-tests/SCTP/README.SCTP_in_ACE b/ACE/performance-tests/SCTP/README.SCTP_in_ACE new file mode 100644 index 00000000000..b51cd6b3fe6 --- /dev/null +++ b/ACE/performance-tests/SCTP/README.SCTP_in_ACE @@ -0,0 +1,269 @@ +Release 1.1 - June 2003 + +Overview of SCTP sockets +------------------------ + +In order to support multiple implementations of SCTP, we had to standardize +on a set of semantics. + +The IETF sockets draft states that service type SOCK_SEQPACKET indicates a UDP +style socket (i.e. connection-less), while service type SOCK_STREAM indicates +a TCP style socket. However, this conflicts with the POSIX definition for +SOCK_SEQPACKET as connection-oriented. + +In ACE we choose to support the standard POSIX definition. In doing so, certain +socket semantics will be guaranteed regardless of implementation. + + [1] SOCK_SEQPACKET sockets will always be message-based, + connection-oriented, and reliable. + + [2] SOCK_STREAM sockets will be message-based or byte-stream based, + connection-oriented, and reliable. + + +SCTP Features Accessible Within ACE +----------------------------------- + + * SOCK_STREAM (byte stream oriented or msg oriented) data transport service + + * SOCK_SEQPACKET (message oriented) data transport service (this is + the service used by TAO's SCIOP pluggable protocol) + + * Explicit control of binding network interfaces (all, one, or any + subset) to server-side passive- and active- mode sockets on + multi-homed machines. (for SOCK_SEQPACKET service only. The + SOCK_STREAM service may only bind one or all interfaces because we + avoided changing the interface of ACE_SOCK_Acceptor.) + + * Setting and getting of all parameters exposed by SCTP + (e.g. retransmission timeout (RTO)) via ACE_SOCK::set_option(...) + and ACE_SOCK::get_option(...) for both SOCK_STREAM and + SOCK_SEQPACKET sockets. You must set the socket level appropriately. + + * Multiplexing of lightweight "SCTP Streams" (over the SOCK_STREAM + and SOCK_SEQPACKET services) via ACE_SOCK::set_option(...) + + * Network path multiplexing (provided opaquely by the protocol---no + explicit support required in ACE other than the ability to + configure various parameters and the set of interfaces as described + above) + + +Supported SCTP Implementations +------------------------------ + + * OpenSS7's Linux Implementation (Berkeley UNIX Network API) + Linux 2.4.18 patch available at: http://www.openss7.org/linux-sctp-0.2.14.tgz + (as of April 2003) + + * The LKSCTP Linux Kernel Implementation (IETF Sockets Draft API compliant) + Available in the Linux 2.5 kernels (http://www.kernel.org/) + Tools/Libs available at http://lksctp.sourceforge.net/ + (All socket interfaces are message-based -- please see README.LKSCTP) + + +BUGS +---- + + * OpenSS7 BUGS + + - protocol crashes when transmitting message sizes greater than + PATH MTU in the presence of network failures (message size + includes SCTP and IP headers and data.) + + * LKSCTP BUGS + + - certain combinations of SCTP parameters will cause a kernel panic + (ie. setting rto_initial, rto_max, or rto_min to 0) + + +TO-DO +---- + + * Provide explicit control of binding network interfaces to + client-side active-mode sockets on multi-homed machines. Current + implementation supports all interfaces but not restriction to one + or to an arbitrary subset. (This will be done for SOCK_SEQPACKET + service only. We want to avoid changing the existing interfaces for + the SOCK_STREAM service). + + * Integrate management and use of "SCTP Streams" into the + ACE_SOCK_SEQPACK_* wrapper-facade. (currently they can only be + accessed indirectly through ACE_SOCK::set_option(...)) + + * Support SOCK_RDM service (connection-less) within ACE for OpenSS7. + + * Convert ATL's histogram utility (located under + performance-tests/SCTP) into a ACE utility and integrate with other + ACE provided statistics classes. + + * Support Draft API msg notifications via sendmsg() and recvmsg(). + + +USAGE +----- + + SOCK_STREAM - Use the ACE_SOCK_Connector, ACE_SOCK_Stream and + ACE_SOCK_Acceptor classes. In ACE_SOCK_Connector pass + the value IPPROTO_SCTP for the protocol parameter in + either the constructor or the connect(...) method + as shown in SOCK_STREAM_clt.cpp. In ACE_SOCK_Acceptor + pass the value IPPROTO_SCTP for the protocol parameter + in either the constructor or the open(...) method + as shown in SOCK_STREAM_srv.cpp. + + You must include the file sctp.h in order for + IPPROTO_SCTP to be defined. This file should be under + /usr/include/netinet + + Aside from these changes, the classes can be used as + they are under TCP (the protocol they use by + default). Be cautious to use SCTP protocol options when + setting socket options on SCTP sockets (e.g., use + SCTP_NODELAY, not TCP_NODELAY, to disable Nagle's + algorithm on an SCTP socket.) + + SOCK_SEQPACKET - Use the ACE_SOCK_SEQPACK_Connector, + ACE_SOCK_SEQPACK_Association, and + ACE_SOCK_SEQPACK_Acceptor classes, which parallel + the familiar ACE_SOCK_Connector, ACE_SOCK_Stream, + and ACE_SOCK_Acceptor classes, respectively. Please + see SOCK_SEQPACK_clt.cpp and SOCK_SEQPACK_srv.cpp for + more details. + + In the special case where you want to specify a set + of interfaces---other than one or all + interfaces---for an ACE_SOCK_SEQPACK_Acceptor, use + an ACE_Multihomed_INET_Addr in place of the familiar + ACE_INET_Addr. (See SOCK_SEQPACK_srv.cpp for an + example.) + + SCTP associations may have more than one local and + more than one remote address bound to them. + Accordingly, ACE_SOCK_SEQPACK_Association provides + methods get_local_addrs(...) and + get_remote_addrs(...). These methods return the list + of local and remote addresses bound to an active + mode SCTP socket. Alternately, the familiar + ACE_SOCK::get_local_addr(...) and + ACE_SOCK::get_remote_addr(...) methods will work + properly with an active mode SCTP socket, but each + will only return a single address. These functions + are only NOT available on ACE_SOCK_SEQPACK_Acceptor + even though that is an SCTP socket as well. This is + because the functions getpeername() and + getsockname() called on a passive SCTP acceptor + socket returns the same values as a TCP socket. As + such, the current ACE methods get_local_addr() and + get_remote_addr() defined in ACE_SOCK are sufficient. + + +DESIGN +------ + +SCTP supports two types of network service: SOCK_STREAM and +SOCK_SEQPACKET. To integrate SCTP's SOCK_STREAM service into ACE we +had to make a small modification to the current SOCK_STREAM wrapper +facade. We had to add a protocol parameter to one constructor and one +connect method of the ACE_SOCK_Connector class. After this +modification the ACE SOCK_STREAM wrapper facade worked properly over +both TCP and SCTP. + +To integrate SCTP's SOCK_SEQPACKET service into ACE we had to create a +new wrapper facade, which we refer to as SOCK_SEQPACK. We closely +emulated the current SOCK_STREAM wrapper facade to develop our new +SOCK_SEQPACK wrapper facade. SOCK_SEQPACK_wrapper_facade.jpg depicts +the classes that implement this new wrapper facade. Also indicated are +those methods that have a substantial change from their SOCK_STREAM +wrapper façade counterparts. Not depicted in the figure but noteworthy +is the removal of the QoS enabled constructor and accept method that +were imported to SOCK_SEQPACK_Acceptor from SOCK_Acceptor and the +removal of the QoS enabled constructor and connect method that were +imported into SOCK_SEQPACK_Connector from SOCK_Connector. SOCK_SEQPACK +association provides two methods to get the list of secondary +addresses associated with the local and remote socket (explained in +more detail in the usage section above). + + +To enable the user to fully exploit the network path multiplexing +features of SCTP we created a new subclass of ACE_INET_Addr called +ACE_INET_Multihomed_Addr. This class enables applications to specify +restricted subsets of network interfaces for inclusion on SCTP +associations on the client and server side. Multihomed_INET_Addr +provides a subset of the ACE_INET_Addr API with the addition of +optional parameters for lists of secondary addresses or hostnames. If +just a primary address or hostname is provided +ACE_Multihomed_INET_Addr behaves as an ACE_INET_Addr (in fact it just +populates the base ACE_INET_Addr) This is also depicted in +SOCK_SEQPACK_wrapper_facade.jpg. Multihomed_INET_Addr is only used by +the SOCK_SEQPACK wrapper facade. + + +All SCTP socket options can be read and written from the current +socket options methods provided by ACE_SOCK. + +Finally, our SOCK_SEQPACK wrapper facade does not yet support SCTP +stream multiplexing. + +Here are the files under $(ACE_ROOT) that were either modified or +added. NO files were removed. + +$(ACE_ROOT)/ace/ace_dll.dsp MODIFIED +$(ACE_ROOT)/ace/OS.h MODIFIED +$(ACE_ROOT)/ace/Makefile.ace MODIFIED +$(ACE_ROOT)/ace/SOCK_Connector.h MODIFIED +$(ACE_ROOT)/ace/SOCK_Connector.cpp MODIFIED + +$(ACE_ROOT)/ace/SOCK_SEQPACK_Acceptor.h ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Acceptor.i ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Acceptor.cpp ADDED + +$(ACE_ROOT)/ace/SOCK_SEQPACK_Connector.h ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Connector.i ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Connector.cpp ADDED + +$(ACE_ROOT)/ace/SOCK_SEQPACK_Association.h ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Association.i ADDED +$(ACE_ROOT)/ace/SOCK_SEQPACK_Association.cpp ADDED + +$(ACE_ROOT)/ace/Multihomed_INET_Addr.h ADDED +$(ACE_ROOT)/ace/Multihomed_INET_Addr.i ADDED +$(ACE_ROOT)/ace/Multihomed_INET_Addr.cpp ADDED + +$(ACE_ROOT)/bin/PerlACE/Process_Unix.pm MODIFIED +$(ACE_ROOT)/bin/PerlACE/Process_Win32.pm MODIFIED + +$(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU MODIFIED +$(ACE_ROOT)/include/makeinclude/platform_linux.GNU MODIFIED + +$(ACE_ROOT)/tests/Makefile MODIFIED +$(ACE_ROOT)/tests/Multihomed_INET_Addr_Test.cpp ADDED +$(ACE_ROOT)/tests/Multihomed_INET_Addr_Test.dsp ADDED +$(ACE_ROOT)/tests/SOCK_SEQPACK_Association_Test.cpp ADDED +$(ACE_ROOT)/tests/SOCK_SEQPACK_Association_Test.dsp ADDED +$(ACE_ROOT)/tests/run_test.lst MODIFIED +$(ACE_ROOT)/tests/run_tests.bat MODIFIED +$(ACE_ROOT)/tests/tests.dsw MODIFIED + +$(ACE_ROOT)/performance-tests/SCTP/THANKS ADDED +$(ACE_ROOT)/performance-tests/SCTP/README ADDED +$(ACE_ROOT)/performance-tests/SCTP/README.SCTP ADDED +$(ACE_ROOT)/performance-tests/SCTP/README.SCTP_in_ACE ADDED +$(ACE_ROOT)/performance-tests/SCTP/README.SCTP_PERF_TEST ADDED +$(ACE_ROOT)/performance-tests/SCTP/README.OpenSS7 ADDED +$(ACE_ROOT)/performance-tests/SCTP/README.LKSCTP ADDED + +$(ACE_ROOT)/performance-tests/SCTP/Makefile ADDED +$(ACE_ROOT)/performance-tests/SCTP/run_spectrum.pl ADDED + +$(ACE_ROOT)/performance-tests/SCTP/hist.h ADDED +$(ACE_ROOT)/performance-tests/SCTP/hist.cpp ADDED + +$(ACE_ROOT)/performance-tests/SCTP/Options_Manager.h ADDED +$(ACE_ROOT)/performance-tests/SCTP/Options_Manager.cpp ADDED + +$(ACE_ROOT)/performance-tests/SCTP/SOCK_STREAM_clt.cpp ADDED +$(ACE_ROOT)/performance-tests/SCTP/SOCK_STREAM_srv.cpp ADDED + +$(ACE_ROOT)/performance-tests/SCTP/SOCK_SEQPACK_clt.cpp ADDED +$(ACE_ROOT)/performance-tests/SCTP/SOCK_SEQPACK_srv.cpp ADDED diff --git a/ACE/performance-tests/SCTP/SCTP.mpc b/ACE/performance-tests/SCTP/SCTP.mpc new file mode 100644 index 00000000000..cba52013c32 --- /dev/null +++ b/ACE/performance-tests/SCTP/SCTP.mpc @@ -0,0 +1,61 @@ +// -*- MPC -*- +// $Id$ + +project(*SOCK_STREAM_clt) : aceexe { + avoids += ace_for_tao + exename = SOCK_STREAM_clt + source_files { + SOCK_STREAM_clt.cpp + Options_Manager.cpp + hist.cpp + } + + // Some source code uses the sqrt and pow function and + // some UNIX operating systems need the math library to + // get the symbol. + verbatim(gnuace, local) { + LDLIBS += $(MATHLIB) + } +} + +project(*SOCK_STREAM_srv) : aceexe { + exename = SOCK_STREAM_srv + source_files { + SOCK_STREAM_srv.cpp + Options_Manager.cpp + } +} + +project(*SOCK_SEQPACK_clt) : aceexe { + avoids += ace_for_tao + exename = SOCK_SEQPACK_clt + source_files { + SOCK_SEQPACK_clt.cpp + Options_Manager.cpp + hist.cpp + } + + // Some source code uses the sqrt and pow function and + // some UNIX operating systems need the math library to + // get the symbol. + verbatim(gnuace, local) { + LDLIBS += $(MATHLIB) + } +} + +project(*SOCK_SEQPACK_srv) : aceexe { + avoids += ace_for_tao + exename = SOCK_SEQPACK_srv + source_files { + SOCK_SEQPACK_srv.cpp + Options_Manager.cpp + } +} + +project(*SOCK_SEQPACK_Association_Test) : aceexe { + avoids += ace_for_tao + exename = SOCK_SEQPACK_Association_Test + source_files { + SOCK_SEQPACK_Association_Test.cpp + } +} diff --git a/ACE/performance-tests/SCTP/SOCK_SEQPACK_Association_Test.cpp b/ACE/performance-tests/SCTP/SOCK_SEQPACK_Association_Test.cpp new file mode 100644 index 00000000000..bb423300062 --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_SEQPACK_Association_Test.cpp @@ -0,0 +1,152 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// tests +// +// = FILENAME +// SOCK_SEQPACK_Association_Test.cpp +// +// = DESCRIPTION +// +// Tests the methods get_local_addrs and get_remote_addrs of class +// ACE_SOCK_SEQPACK_Association. +// +// This is not an automated "one-button" test. Rather, it prints +// some output to a log file, so that an interested human can +// inspect the output and get a vague notion of whether or not +// the methods are working properly. +// +// = AUTHOR +// Edward Mulholland (emulholl@atl.lmco.com) +// +// ============================================================================ + +#include "ace/SOCK_SEQPACK_Association.h" +#include "ace/SOCK_SEQPACK_Connector.h" +#include "ace/INET_Addr.h" +#include "ace/Log_Msg.h" + +void dump_names(const ACE_SOCK_SEQPACK_Association& assoc); + +int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int status = 0; // Innocent until proven guilty + + // object that manages the connection to the server + ACE_SOCK_SEQPACK_Connector connector; + + // object that manages the data xfer between the client and server + ACE_SOCK_SEQPACK_Association dataStream; + + // object that represents the server's IP address and port + ACE_INET_Addr serverAddr; + + if (argc < 2) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Usage: SOCK_SEQPACK_Association_Test hostname:port\n"))); + status = 1; + + } else if (serverAddr.set(argv[1])) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_INET_Addr::set"))); + status = 1; + + } else if (connector.connect (dataStream, serverAddr)) { + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_SOCK_SEQPACK_Connector::connect"))); + status = 1; + + } else { + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("Connected to server at %s\n"), + argv[1])); + + dump_names(dataStream); + } + + dataStream.close(); + + return status; +} + +void dump_names(const ACE_SOCK_SEQPACK_Association& assoc) +{ + // Pre-declare for-loop index + size_t i = 0; + + size_t in_out_size = 100; + ACE_INET_Addr in_out[100]; + + // Output char buffer + const size_t outbuf_size = 1024; + ACE_TCHAR outbuf[outbuf_size]; + + // Get local addresses of the association + if (assoc.get_local_addrs(in_out, in_out_size)) { + + ACE_ERROR((LM_ERROR, + "%p\n", + "get_local_addrs")); + return; + } + + ACE_DEBUG((LM_DEBUG, "Called get_local_addrs\n")); + + // Print individual results of get_local_addrs + for (i = 0; i < in_out_size; ++i) { + + if (in_out[i].addr_to_string(outbuf, outbuf_size)) { + + ACE_ERROR((LM_ERROR, + "%p\n", + "addr_to_string")); + return; + } + + ACE_DEBUG((LM_DEBUG, + "get_local_addrs[%i] = %s\n", + i, + outbuf)); + } + + // Reset in_out_size + in_out_size = 100; + + // Get remote addresses of the association + if (assoc.get_remote_addrs(in_out, in_out_size)) { + + ACE_ERROR((LM_ERROR, + "%p\n", + "get_remote_addrs")); + return; + } + + ACE_DEBUG((LM_DEBUG, "Called get_remote_addrs\n")); + + // Print individual results of get_remote_addrs + for (i = 0; i < in_out_size; ++i) { + + if (in_out[i].addr_to_string(outbuf, outbuf_size)) { + + ACE_ERROR((LM_ERROR, + "%p\n", + "addr_to_string")); + return; + } + + ACE_DEBUG((LM_DEBUG, + "get_remote_addrs[%i] = %s\n", + i, + outbuf)); + } +} + + diff --git a/ACE/performance-tests/SCTP/SOCK_SEQPACK_clt.cpp b/ACE/performance-tests/SCTP/SOCK_SEQPACK_clt.cpp new file mode 100644 index 00000000000..fc41f2c7d3a --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_SEQPACK_clt.cpp @@ -0,0 +1,309 @@ +// $Id$ + +#include "ace/OS_Memory.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_SEQPACK_Association.h" +#include "ace/SOCK_SEQPACK_Connector.h" +#include "ace/Log_Msg.h" +#include "ace/CDR_Stream.h" +#include "ace/High_Res_Timer.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/os_include/arpa/os_inet.h" + +// make sure that the code compiles cleanly even if SCTP is not +// available. If SCTP is not installed, program will exit early in +// main() with an error message +#ifdef ACE_HAS_SCTP +extern "C" { +#include <netinet/sctp.h> +}; +#else +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif // !IPPROTO_SCTP +#define SCTP_NODELAY 1 +#endif // ACE_HAS_SCTP + +// class that manages setting of options +#include "Options_Manager.h" + +// histogram code +#include "hist.h" + +// global constants +ACE_CDR::UShort const primerIterations = 100; +// ace/High_Res_Timer.h describes the need for the following. +ACE_UINT32 const microsec_clock_scale_factor = ACE_High_Res_Timer::global_scale_factor(); + +// forward declations of functions. Bodies follow main() to improve +// file readability. +ACE_SCTP::HIST runTest(ACE_SOCK_SEQPACK_Association &); + +int ACE_TMAIN (int argc, ACE_TCHAR **argv){ + + // Initialize the options manager + Options_Manager optsMgr(argc, argv, ACE_TEXT ("client-opts")); + + // show usage is requested + if (optsMgr._usage) { + optsMgr._show_usage(stderr, ACE_TEXT ("client-opts")); + return 1; + } + +#ifndef ACE_HAS_SCTP + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("SCTP was NOT installed/accessible when this binary was compiled.\n")), + 1); +#else + + // check that valid options were specified + if (optsMgr._error) { + ACE_OS::fprintf (stderr, "ERROR: %s\n", ACE_TEXT_ALWAYS_CHAR (optsMgr._error_message)); + return 1; + } + + + // Create the address that we want the client to connect to + ACE_INET_Addr serverAddr(Options_Manager::server_port, + Options_Manager::server_host); + + // Create the address that we want the client to connect FROM + ACE_Multihomed_INET_Addr clientAddr; + clientAddr.set(optsMgr.client_port, + optsMgr.client_connect_addr, + 1, + optsMgr.secondary_connect_addrs, + optsMgr.num_secondary_connect_addrs); + + // object that manages the connection to the server + ACE_SOCK_SEQPACK_Connector connector; + + // object that manages the data xfer between the client and server + ACE_SOCK_SEQPACK_Association dataStream; + + // connect to the server + if (connector.connect (dataStream, + serverAddr, + 0,clientAddr, 0, 0, 0, optsMgr.test_transport_protocol) == -1) /*, // ALL DEFAULT ARGUMENTS + Options_Manager::test_transport_protocol) == -1) */ + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed")), + 1); + + // run the test + ACE_SCTP::HIST testResultsHistogram = 0; + // connection is closed by runTest* functions + testResultsHistogram = runTest(dataStream); + + // report out the test statistics IF NO EXECPTIONS have been thrown + // all histograms created are placed onto a linked list that report() + // can access. So the histogram created in one of the *_test() functions + // will be reported out by the report() call + if (testResultsHistogram) + ACE_SCTP::report(); + + return 0; +#endif /* ACE_HAS_SCTP */ +} + +// create a histogram to store test results +ACE_SCTP::HIST createHistogram(ACE_CDR::ULong messageSize){ + // The histogram created below lives beyond the scope of this + // function. So the memory allocated here cannot be cleaned up when + // this function goes out of scope. Unfortunately the histogram + // implementation does not clean it up either. So it is a small + // memory leak. + char * histName = (char *) malloc(200); + + ACE_OS::sprintf(histName, "%s Unmarshalled Msg Synchronous Latency Test\n\t\t\t\t\t(Message Size %u, Message Type octet)", + "ACE", messageSize); + + // actually create the histogram + ACE_SCTP::HIST createdHist = ACE_SCTP::histogram(histName, + Options_Manager::histogram_bin_count, + Options_Manager::histogram_min_bin, + Options_Manager::histogram_max_bin); + + // set the maximum number of outliers to maintain in the histogram + ACE_SCTP::set_outer(Options_Manager::histogram_num_outliers, createdHist); + + return (createdHist); +} + +// send the test header (only contains number of iterations) +int sendHeader(ACE_SOCK_SEQPACK_Association & stream) { + + // create an ACE CDR output stream and place the header information + // into it + ACE_OutputCDR hdrCDR; + hdrCDR << ACE_OutputCDR::from_boolean (ACE_CDR_BYTE_ORDER); + hdrCDR << ACE_CDR::ULong(Options_Manager::test_iterations+primerIterations); + if (!hdrCDR.good_bit()) + return (0); + + // send the header to the server (HEADER is 8 BYTES LONG) + size_t bt; + if (stream.send_n(hdrCDR.begin(), 0, &bt) == -1) + return 0; + + return 1; +} + +// conduct the UnMarshalled Octet performance test using separate +// send_n calls with Nagle's algorithm disabled +ACE_SCTP::HIST runUnmarshalledOctetTest(ACE_CDR::Octet *buf, size_t seqLen, ACE_SOCK_SEQPACK_Association & stream){ + + ACE_CDR::ULong const testIterations = Options_Manager::test_iterations; + + size_t bt; + ACE_CDR::ULong cnt = 0; + // variables for the timing measurements + ACE_hrtime_t startTime, endTime; + ACE_CDR::Double messageLatency_usec = 0.0; + ACE_CDR::ULong msgLen = seqLen*ACE_CDR::OCTET_SIZE; + + // explicity configure Nagling. Default is + // Options_Manager::test_enable_nagle=0 so default configurations is + // NO NAGLING + ACE_CDR::Long nagle; + if (Options_Manager::test_enable_nagle) + nagle=0; + else + nagle=1; + if (-1 == stream.set_option(IPPROTO_SCTP, SCTP_NODELAY, &nagle, sizeof nagle)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("set_option")), + 0); + + // prime the client and server before starting the test + for(cnt=0;cnt<primerIterations;++cnt){ + + // send message size + // TODO : The message length should be CDR encoded + ACE_CDR::ULong msgLenExpressed = ACE_HTONL(msgLen); + if (-1 == stream.send_n (&msgLenExpressed, ACE_CDR::LONG_SIZE, 0, &bt)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send_n")), + 0); + + // send a message + if (-1 == stream.send_n (buf, msgLen, 0, &bt)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send_n")), + 0); + + // block for a Short reply + ACE_CDR::Short reply; + if ((stream.recv_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + } + + // AFTER PRIMING THE PUMP CREATE THE HISTOGRAM + ACE_SCTP::HIST aceStream_hist = 0; + aceStream_hist = createHistogram(msgLen); + if (0 == aceStream_hist) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("histogram create failed")), + 0); + + iovec iov[2]; + // PERFORMANCE TEST LOOP + for (cnt = 0; cnt < testIterations; ++cnt){ + + // get the start time + startTime = ACE_OS::gethrtime(); + if (!startTime) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::gethrtime()")), + 0); + + ACE_CDR::ULong msgLenExpressed = ACE_HTONL(msgLen); + iov[0].iov_base = reinterpret_cast<char *> (&msgLenExpressed); + iov[0].iov_len = ACE_CDR::LONG_SIZE; + iov[1].iov_base = reinterpret_cast<char *> (buf); + iov[1].iov_len = msgLen; + + if (-1 == stream.sendv_n (iov, 2)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send_n")), + 0); + + // block for a Short reply + ACE_CDR::Short reply; + if ((stream.recv_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + + // get the end time + endTime = ACE_OS::gethrtime(); + if (!endTime) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("ACE_OS::gethrtime()")), + 0); + + // compute the message latency in micro-seconds + messageLatency_usec = + + (static_cast<double> (ACE_UINT64_DBLCAST_ADAPTER(endTime)) - + static_cast<double> (ACE_UINT64_DBLCAST_ADAPTER(startTime))) + + / microsec_clock_scale_factor; + + + // record the message latency in the histogram + ACE_SCTP::record(messageLatency_usec, aceStream_hist); + } + + // THE HEADER MESSAGE SENT TO THE SERVER CONTAINED THE NUMBER OF + // PRIMER AND TEST MESSAGES TO BE SENT AFTER WHICH THE SERVER WILL + // CLOSE THE STREAM SO ONCE WE REACH THIS POINT THE STREAM IS NO + // LONGER VALID AND WE CLOSE IT. + stream.close(); + + // allocated by runTest + delete[] buf; + return aceStream_hist; +} + +// sends the test information to the server and calls the correct test +// function. +ACE_SCTP::HIST runTest(ACE_SOCK_SEQPACK_Association & stream) +{ + size_t msgLen = 1; + for (int i=1; i <= Options_Manager::payload_size_power_of_2; i++) + msgLen *= 2; + + + + // send a header to the server that contains test parameters + if (sendHeader(stream) < 0) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("Could NOT Send CDR Encoded Header")), + 0); + + // create the forward message buffer + + ACE_CDR::Octet *o_arr; + ACE_CDR::ULong seqLen = msgLen/ACE_CDR::OCTET_SIZE; + ACE_NEW_RETURN(o_arr, + ACE_CDR::Octet[seqLen], + 0); + // o_arr is deleted in run_octet_test + return(runUnmarshalledOctetTest(o_arr, seqLen, stream)); +} diff --git a/ACE/performance-tests/SCTP/SOCK_SEQPACK_srv.cpp b/ACE/performance-tests/SCTP/SOCK_SEQPACK_srv.cpp new file mode 100644 index 00000000000..f0826eeb9f1 --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_SEQPACK_srv.cpp @@ -0,0 +1,365 @@ +// $Id$ + +#include "ace/Multihomed_INET_Addr.h" +#include "ace/SOCK_SEQPACK_Association.h" +#include "ace/SOCK_SEQPACK_Acceptor.h" +#include "ace/Log_Msg.h" +#include "ace/Thread_Manager.h" +#include "ace/Handle_Set.h" +#include "ace/CDR_Stream.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/os_include/arpa/os_inet.h" +#include "ace/OS_NS_sys_select.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_arpa_inet.h" + +// make sure that the code compiles cleanly even if SCTP is not +// available. If SCTP is not installed, program will exit early in +// main() with an error message +#ifdef ACE_HAS_SCTP +extern "C" { +#include <netinet/sctp.h> +}; +#else +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif // !IPPROTO_SCTP +#define SCTP_NODELAY 1 +#endif + +// class that manages setting of options +#include "Options_Manager.h" + +#ifdef ACE_HAS_SCTP + +// structure to bundle arguments for thread functions +struct ArgStruct { + ACE_SOCK_SEQPACK_Association * stream; + ACE_CDR::ULong numIters; +}; + +// thread function that serves the client for the UnMarshalled Octet +// test +static ACE_THR_FUNC_RETURN unmarshalledOctetServer (void *arg){ + + // unbundle the arguments + ArgStruct * args = reinterpret_cast<ArgStruct *> (arg); + ACE_SOCK_SEQPACK_Association * dataModeStream = args->stream; + ACE_CDR::ULong numIterations = args->numIters; + delete args; + + // serve the client for numIterations synchronous invocations + do { + + // READ A MESSAGE FROM THE CLIENT + + size_t bt; + ACE_CDR::ULong msgBufSize=0; + // read the size of the buffer to follow + if ((dataModeStream->recv_n(&msgBufSize, ACE_CDR::LONG_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + msgBufSize = ACE_NTOHL(msgBufSize); + + // allocate the buffer for the message payload + ACE_CDR::Octet * msgBuf = 0; + ACE_NEW_RETURN(msgBuf, + ACE_CDR::Octet[msgBufSize], + 0); + + // read the buffer + if ((dataModeStream->recv_n(msgBuf, msgBufSize, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + + // clean up the allocated buffer + delete[] msgBuf; + + // SEND A REPLY TO THE CLIENT + + // send back a 2 byte reply + ACE_CDR::Short reply; + if ((dataModeStream->send_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send_n")), + 0); + + } while (--numIterations); + + // close and destroy the stream + dataModeStream->close(); + delete dataModeStream; + + return 0; +} + +// sets up the dataModeSocket Stream, reads the test header infomation +// and launches a thread to handle the requested test. +static void run_server (ACE_HANDLE handle) +{ + ACE_INET_Addr cli_addr; + // create a new stream and initialized with the handle returned by + // accept + ACE_SOCK_SEQPACK_Association * dataModeStream = new ACE_SOCK_SEQPACK_Association; + dataModeStream->set_handle (handle); + + // Make sure we're not in non-blocking mode. + if (dataModeStream->disable (ACE_NONBLOCK) == -1){ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("disable"))); + return; + } + else if (dataModeStream->get_remote_addr (cli_addr) == -1){ + ACE_ERROR ((LM_ERROR, + "%p\n", + "get_remote_addr")); + return; + } + + // explicity configure Nagling. Default is + // Options_Manager::test_enable_nagle=0 so default configurations is + // NO NAGLING + ACE_CDR::Long nagle; + if (Options_Manager::test_enable_nagle) + nagle=0; + else + nagle=1; + + if (-1 == dataModeStream->set_option(IPPROTO_SCTP, SCTP_NODELAY, &nagle, sizeof nagle)){ + // can't ise ACE_ERROR_RETURN b/c function has void return value + ACE_ERROR((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("set_option"))); + return; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT("(%P|%t) client %C connected from %d\n"), + cli_addr.get_host_name (), + cli_addr.get_port_number ())); + + // hdr bufSize is hardcoded to 8 bytes + // (4 for a CDR-encoded boolean and 4 for a CDR-encoded ULong) + ACE_CDR::ULong hdrBufSize = 8; + // allocate a raw buffer large enough to receive the header and be + // properly aligned for the CDR decoding. + ACE_CDR::Char * hdrBuf= new ACE_CDR::Char[hdrBufSize+ACE_CDR::MAX_ALIGNMENT]; + // align the raw buffer before reading data into it. + char * hdrBuf_a = ACE_ptr_align_binary(hdrBuf, ACE_CDR::MAX_ALIGNMENT); + + size_t bt; + // read the header + if ((dataModeStream->recv_n(hdrBuf_a, hdrBufSize, 0, &bt)) == -1){ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n"))); + return; + } + + // pass the CDR encoded data into an ACE_InputCDR class. hdrCDR does + // NOT copy this data. Nor does it delete. It assumes the buffer + // remains valid while it is in scope. + ACE_InputCDR hdrCDR(hdrBuf_a, hdrBufSize); + + ACE_CDR::Boolean byteOrder; + ACE_CDR::ULong numIterations; + + // extract the data + hdrCDR >> ACE_InputCDR::to_boolean (byteOrder); + hdrCDR.reset_byte_order(byteOrder); + hdrCDR >> numIterations; + + // make sure the stream is good after the extractions + if (!hdrCDR.good_bit()){ + ACE_ERROR((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("hdrCDR"))); + + return; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Test for %u iterations\n"), + numIterations)); + + // deallocate the header buffer + delete[] hdrBuf; + + // bundle up the arguments + ArgStruct * args = new ArgStruct; + args->stream = dataModeStream; + args->numIters = numIterations; + +#if defined (ACE_HAS_THREADS) + // Spawn a new thread and run the new connection in that thread of + // control using the <server> function as the entry point. + if (ACE_Thread_Manager::instance ()->spawn (unmarshalledOctetServer, + reinterpret_cast<void *> (args), + THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("spawn"))); +#else + (*unmarshalledOctetServer) (reinterpret_cast<void *> (args)); +#endif /* ACE_HAS_THREADS */ + +} + +#endif + + +int ACE_TMAIN (int argc, ACE_TCHAR **argv){ + + Options_Manager optsMgr(argc, argv, ACE_TEXT ("server-opts")); + + // show usage is requested + if (optsMgr._usage) { + optsMgr._show_usage(stderr, ACE_TEXT ("server-opts")); + return 1; + } + +#ifndef ACE_HAS_SCTP + // SCTP is not installed. Exit with informative error message. + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("SCTP was NOT installed/accessible when this binary was compiled.\n")), + 1); +#else + + // check that valid options were specified + if (optsMgr._error) { + ACE_OS::fprintf (stderr, "ERROR: %s\n", ACE_TEXT_ALWAYS_CHAR (optsMgr._error_message)); + return 1; + } + + // this is the socket that the server will listen on + ACE_SOCK_SEQPACK_Acceptor acceptor_socket; + + // Create the address that we want to listen for connections on. If + // server_accept_addr=INADDR_ANY (the default), SCTP will listen for + // connections on all IP interfaces. If an address is specified, + // SCTP will listen for connections on that address ONLY. + ACE_Multihomed_INET_Addr serverAddr; + + serverAddr.set(optsMgr.server_port, + optsMgr.server_accept_addr, + 1, + optsMgr.secondary_accept_addrs, + optsMgr.num_secondary_accept_addrs); + + // this operation creates a socket, binds the specified internet + // address to it and calls listen. As this is a wrapper facade + // approach, the ACE_OS::{socket,bind,listen} calls are invoked in + // the implementation of open. + if (acceptor_socket.open(serverAddr, 1, + AF_INET, + ACE_DEFAULT_BACKLOG, + optsMgr.test_transport_protocol) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 1); + + // this function checks that the port that was actually bound was + // the port we asked for. Apparently some operating systems will + // automatically select new ports if the specified port is currently + // used. + if (acceptor_socket.get_local_addr(serverAddr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr")), + 1); + + sockaddr_in *addresses = new sockaddr_in[ 1 + serverAddr.get_num_secondary_addresses() ]; + serverAddr.get_addresses( addresses, 1 + serverAddr.get_num_secondary_addresses() ) ; + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Accepting connections, using %C on ") + ACE_TEXT ("port %u on interfaces %C"), + (optsMgr.test_transport_protocol == IPPROTO_SCTP) ? "IPPROTO_SCTP" : "IPPROTO_TCP", + serverAddr.get_port_number(), + ACE_OS::inet_ntoa( addresses[0].sin_addr) )); + + unsigned int i; + for(i=1; i <= serverAddr.get_num_secondary_addresses() ; ++i ) { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT (" and %C"), + ACE_OS::inet_ntoa (addresses[i].sin_addr))); + } + ACE_DEBUG((LM_DEBUG, ACE_TEXT ("\n"))); + + delete[] addresses; + + // this is the stream object that will associated with a completed + // connection (aka the data mode socket). It will be set when accept + // is called below. + ACE_SOCK_SEQPACK_Association new_stream; + + // a file decriptor set + ACE_Handle_Set handle_set; + // add the acceptor socket to the file descriptor set. + handle_set.set_bit(acceptor_socket.get_handle()); + + for (;;){ + + ACE_Time_Value timeout(ACE_DEFAULT_TIMEOUT); + ACE_Handle_Set temp = handle_set; + + // wait for connect() call from client. In the original test there + // were two different acceptor sockets for two different + // services. So select was needed to wait on both sockets + // simultaneously. In this test we could just call accept on the + // one socket. + int result = ACE_OS::select((int) (acceptor_socket.get_handle()) +1, + (fd_set *) temp, + 0, + 0, + timeout); + + // check that select did not end with an error. + if (result == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select"))); + // check to see if select timed out. + else if (result == 0){ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) select timed out\n"))); + + } + else { // case where a file descriptor was actually set + if (!(temp.is_set(acceptor_socket.get_handle()))){ + // CANNOT BE REACHED + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select: NO ERROR BUT NO FD SET"))); + } else { + // call accept to set up the new stream. + if (acceptor_socket.accept(new_stream) == -1) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("accept"))); + continue; + } + else{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawning server\n"))); + + } + // Run the server. + run_server (new_stream.get_handle ()); + } + + } + + } + return 0; +#endif /* ACE_HAS_SCTP */ +} diff --git a/ACE/performance-tests/SCTP/SOCK_SEQPACK_wrapper_facade.jpg b/ACE/performance-tests/SCTP/SOCK_SEQPACK_wrapper_facade.jpg Binary files differnew file mode 100755 index 00000000000..3cb82002fe5 --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_SEQPACK_wrapper_facade.jpg diff --git a/ACE/performance-tests/SCTP/SOCK_STREAM_clt.cpp b/ACE/performance-tests/SCTP/SOCK_STREAM_clt.cpp new file mode 100644 index 00000000000..9e560409c8f --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_STREAM_clt.cpp @@ -0,0 +1,318 @@ +// $Id$ + +#include "ace/INET_Addr.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Connector.h" +#include "ace/Log_Msg.h" +#include "ace/CDR_Stream.h" +#include "ace/High_Res_Timer.h" +#include "ace/OS_Memory.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/os_include/netinet/os_tcp.h" +#include "ace/os_include/arpa/os_inet.h" + +// make sure that the code compiles cleanly even if SCTP is not +// available. If SCTP is not installed, program will exit early in +// main() with an error message +#ifdef ACE_HAS_SCTP +extern "C" { +#include <netinet/sctp.h> +}; +#else +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif +#define SCTP_NODELAY -1 +#endif + +// class that manages setting of options +#include "Options_Manager.h" + +// histogram code +#include "hist.h" + +// global constants +ACE_CDR::UShort const primerIterations = 100; +// ace/High_Res_Timer.h describes the need for the following. +ACE_UINT32 const microsec_clock_scale_factor = ACE_High_Res_Timer::global_scale_factor(); + +// forward declations of functions. Bodies follow main() to improve +// file readability. +ACE_SCTP::HIST runTest(ACE_SOCK_Stream &); + +int ACE_TMAIN (int argc, ACE_TCHAR **argv){ + + // Initialize the options manager + Options_Manager optsMgr(argc, argv, ACE_TEXT ("client-opts")); + + // show usage if requested + if (optsMgr._usage) { + optsMgr._show_usage(stderr, ACE_TEXT ("client-opts")); + return 1; + } + + // If SCTP is not installed then terminate the program, unless TCP + // was specified. +#ifndef ACE_HAS_SCTP + if (optsMgr.test_transport_protocol == IPPROTO_SCTP) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("SCTP was NOT installed when this binary was compiled.\nSOCK_STREAM_clt may still be run using TCP via the '-t tcp' option.\n")), + 1); +#endif + + // check that valid options were specified + if (optsMgr._error) { + ACE_OS::fprintf (stderr, "ERROR: %s\n", ACE_TEXT_ALWAYS_CHAR (optsMgr._error_message)); + return 1; + } + + + // Create the address that we want the client to connect to + ACE_INET_Addr serverAddr(Options_Manager::server_port, + Options_Manager::server_host); + + // Create the address that we want the client to connect FROM + ACE_INET_Addr clientAddr(Options_Manager::client_port, + Options_Manager::client_connect_addr); + + // object that manages the connection to the server + ACE_SOCK_Connector connector; + + // object that manages the data xfer between the client and server + ACE_SOCK_Stream dataStream; + + // connect to the server + if (connector.connect (dataStream, + serverAddr, + 0,clientAddr, 0, 0, 0, // ALL DEFAULT ARGUMENTS + Options_Manager::test_transport_protocol) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("connection failed")), + 1); + + // run the test + ACE_SCTP::HIST testResultsHistogram = 0; + // connection is closed by runTest* functions + testResultsHistogram = runTest(dataStream); + + // report out the test statistics. + // all histograms created are placed onto a linked list that report() + // can access. So the histogram created in one of the *_test() functions + // will be reported out by the report() call + if (testResultsHistogram) + ACE_SCTP::report(); + + return 0; +} + + // create a histogram to store test results + ACE_SCTP::HIST createHistogram(ACE_CDR::ULong messageSize){ + + // The histogram created below lives beyond the scope of this + // function. So the memory allocated here cannot be cleaned up when + // this function goes out of scope. Unfortunately the histogram + // implementation does not clean it up either. So it is a small + // memory leak. + char * histName = (char *) malloc(200); + + ACE_OS::sprintf(histName, "%s Unmarshalled Msg Synchronous Latency Test\n\t\t\t\t\t(Message Size %u, Message Type octet)", + "ACE", messageSize); + + // actually create the histogram + ACE_SCTP::HIST createdHist = ACE_SCTP::histogram(histName, + Options_Manager::histogram_bin_count, + Options_Manager::histogram_min_bin, + Options_Manager::histogram_max_bin); + + // set the maximum number of outliers to maintain in the histogram + ACE_SCTP::set_outer(Options_Manager::histogram_num_outliers, createdHist); + + return (createdHist); +} + +// send the test header (only contains number of iterations) +int sendHeader(ACE_SOCK_Stream & stream) { + + // create an ACE CDR output stream and place the header information + // into it + ACE_OutputCDR hdrCDR; + hdrCDR << ACE_OutputCDR::from_boolean (ACE_CDR_BYTE_ORDER); + hdrCDR << ACE_CDR::ULong(Options_Manager::test_iterations+primerIterations); + if (!hdrCDR.good_bit()) + return (0); + + // send the header to the server (HEADER IS 8 BYTES LONG) + size_t bt; + if (stream.send_n(hdrCDR.begin(), 0, &bt) == -1) + return 0; + + return 1; +} + +// conduct the UnMarshalled Octet performance test using separate +// send_n calls with Nagle's algorithm disabled +ACE_SCTP::HIST runUnmarshalledOctetTest(ACE_CDR::Octet *buf, size_t seqLen, ACE_SOCK_Stream & stream){ + + ACE_CDR::ULong const testIterations = Options_Manager::test_iterations; + + size_t bt; + ACE_CDR::ULong cnt = 0; + // variables for the timing measurements + ACE_hrtime_t startTime, endTime; + ACE_CDR::Double messageLatency_usec = 0.0; + ACE_CDR::ULong msgLen = seqLen*ACE_CDR::OCTET_SIZE; + + // explicity configure Nagling. Default is + // Options_Manager::test_enable_nagle=0 so default configurations is + // NO NAGLING + ACE_CDR::Long nagle; + if (Options_Manager::test_enable_nagle) + nagle=0; + else + nagle=1; + if (Options_Manager::test_transport_protocol == IPPROTO_SCTP){ + // default - sctp case + if (-1 == stream.set_option(IPPROTO_SCTP, SCTP_NODELAY, &nagle, sizeof nagle)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "set_option"), + 0); + } else { + // tcp case + if (-1 == stream.set_option(IPPROTO_TCP, TCP_NODELAY, &nagle, sizeof nagle)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "set_option"), + 0); + } + + // prime the client and server before starting the test + for(cnt=0;cnt<primerIterations;++cnt){ + + // send message size + // TODO : The message length should be CDR encoded + ACE_CDR::ULong msgLenExpressed = ACE_HTONL(msgLen); + if (-1 == stream.send_n (&msgLenExpressed, ACE_CDR::LONG_SIZE, 0, &bt)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send_n"), + 0); + + // send a message + if (-1 == stream.send_n (buf, msgLen, 0, &bt)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send_n"), + 0); + + // block for a Short reply + ACE_CDR::Short reply; + if ((stream.recv_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "recv_n"), + 0); + } + + // AFTER PRIMING THE PUMP CREATE THE HISTOGRAM + ACE_SCTP::HIST aceStream_hist = 0; + aceStream_hist = createHistogram(msgLen); + if (0 == aceStream_hist) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "histogram create failed"), + 0); + + iovec iov[2]; + // PERFORMANCE TEST LOOP + for (cnt = 0; cnt < testIterations; ++cnt){ + + // get the start time + startTime = ACE_OS::gethrtime(); + if (!startTime) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_OS::gethrtime()"), + 0); + + + ACE_CDR::ULong msgLenExpressed = ACE_HTONL(msgLen); + iov[0].iov_base = reinterpret_cast<char *> (&msgLenExpressed); + iov[0].iov_len = ACE_CDR::LONG_SIZE; + iov[1].iov_base = reinterpret_cast<char *> (buf); + iov[1].iov_len = msgLen; + + if (-1 == stream.sendv_n (iov, 2)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send_n"), + 0); + + // block for a Short reply + ACE_CDR::Short reply; + if ((stream.recv_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "recv_n"), + 0); + + // get the end time + endTime = ACE_OS::gethrtime(); + if (!endTime) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_OS::gethrtime()"), + 0); + + // compute the message latency in micro-seconds + messageLatency_usec = + + (static_cast<double> (ACE_UINT64_DBLCAST_ADAPTER(endTime)) - + static_cast<double> (ACE_UINT64_DBLCAST_ADAPTER(startTime))) + + / microsec_clock_scale_factor; + + // record the message latency in the histogram + ACE_SCTP::record(messageLatency_usec, aceStream_hist); + } + + // THE HEADER MESSAGE SENT TO THE SERVER CONTAINED THE NUMBER OF + // PRIMER AND TEST MESSAGES TO BE SENT AFTER WHICH THE SERVER WILL + // CLOSE THE STREAM SO ONCE WE REACH THIS POINT THE STREAM IS NO + // LONGER VALID AND WE CLOSE IT. + stream.close(); + + // allocated by runTest + delete[] buf; + return aceStream_hist; +} + +// sends the test information to the server and calls the correct test +// function. +ACE_SCTP::HIST runTest(ACE_SOCK_Stream & stream) +{ + + size_t msgLen = 1; + for (int i=1; i <= Options_Manager::payload_size_power_of_2; i++) + msgLen *= 2; + + // send a header to the server that contains test parameters + if (sendHeader(stream) < 0) + ACE_ERROR_RETURN((LM_ERROR, + "Could NOT Send CDR Encoded Header"), + 0); + + // create the forward message buffer + + ACE_CDR::Octet *o_arr; + ACE_CDR::ULong seqLen = msgLen/ACE_CDR::OCTET_SIZE; + ACE_NEW_RETURN(o_arr, + ACE_CDR::Octet[seqLen], + 0); + // o_arr is deleted in run_octet_test + return(runUnmarshalledOctetTest(o_arr, seqLen, stream)); +} diff --git a/ACE/performance-tests/SCTP/SOCK_STREAM_srv.cpp b/ACE/performance-tests/SCTP/SOCK_STREAM_srv.cpp new file mode 100644 index 00000000000..a9ff3959a42 --- /dev/null +++ b/ACE/performance-tests/SCTP/SOCK_STREAM_srv.cpp @@ -0,0 +1,358 @@ +// $Id$ + +#include "ace/INET_Addr.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Log_Msg.h" +#include "ace/Thread_Manager.h" +#include "ace/Handle_Set.h" +#include "ace/CDR_Stream.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/os_include/netinet/os_tcp.h" +#include "ace/os_include/arpa/os_inet.h" +#include "ace/OS_NS_sys_select.h" + +// make sure that the code compiles cleanly even if SCTP is not +// available. If SCTP is not installed, program will exit early in +// main() with an error message +#ifdef ACE_HAS_SCTP +extern "C" { +#include <netinet/sctp.h> +}; +#else +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 /* always the same value on every platform */ +#endif +#define SCTP_NODELAY -1 +#endif + +// class that manages setting of options +#include "Options_Manager.h" + +// structure to bundle arguments for thread functions +struct ArgStruct { + ACE_SOCK_Stream * stream; + ACE_CDR::ULong numIters; +}; + +// thread function that serves the client for the UnMarshalled Octet +// test +static ACE_THR_FUNC_RETURN unmarshalledOctetServer (void *arg){ + + // unbundle the arguments + ArgStruct * args = reinterpret_cast<ArgStruct *> (arg); + ACE_SOCK_Stream * dataModeStream = args->stream; + ACE_CDR::ULong numIterations = args->numIters; + delete args; + + // serve the client for numIterations synchronous invocations + do { + + // READ A MESSAGE FROM THE CLIENT + + size_t bt; + ACE_CDR::ULong msgBufSize=0; + // read the size of the buffer to follow + if ((dataModeStream->recv_n(&msgBufSize, ACE_CDR::LONG_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + msgBufSize = ACE_NTOHL(msgBufSize); + + // allocate the buffer for the message payload + ACE_CDR::Octet * msgBuf = 0; + ACE_NEW_RETURN(msgBuf, + ACE_CDR::Octet[msgBufSize], + 0); + + // read the buffer + if ((dataModeStream->recv_n(msgBuf, msgBufSize, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n")), + 0); + + // clean up the allocated buffer + delete[] msgBuf; + + // SEND A REPLY TO THE CLIENT + + // send back a 2 byte reply + ACE_CDR::Short reply; + if ((dataModeStream->send_n(&reply, ACE_CDR::SHORT_SIZE, 0, &bt)) == -1) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("send_n")), + 0); + + } while (--numIterations); + + // close and destroy the stream + dataModeStream->close(); + delete dataModeStream; + + return 0; +} + +// sets up the dataModeSocket Stream, reads the test header infomation +// and launches a thread to handle the requested test. +static void run_server (ACE_HANDLE handle) +{ + ACE_INET_Addr cli_addr; + // create a new stream and initialized with the handle returned by + // accept + ACE_SOCK_Stream * dataModeStream = new ACE_SOCK_Stream; + dataModeStream->set_handle (handle); + + // Make sure we're not in non-blocking mode. + if (dataModeStream->disable (ACE_NONBLOCK) == -1){ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("disable"))); + return; + } + else if (dataModeStream->get_remote_addr (cli_addr) == -1){ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_remote_addr"))); + return; + } + + // explicity configure Nagling. Default is + // Options_Manager::test_enable_nagle=0 so default configurations is + // NO NAGLING + ACE_CDR::Long nagle; + if (Options_Manager::test_enable_nagle) + nagle=0; + else + nagle=1; + + if (Options_Manager::test_transport_protocol == IPPROTO_SCTP){ + // default - sctp case + if (-1 == dataModeStream->set_option(IPPROTO_SCTP, SCTP_NODELAY, &nagle, sizeof nagle)){ + ACE_ERROR((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("set_option"))); + return; + } + } else { + // tcp case + if (-1 == dataModeStream->set_option(IPPROTO_TCP, TCP_NODELAY, &nagle, sizeof nagle)){ + ACE_ERROR ((LM_ERROR, + "%p\n", + "set_option")); + return; + } + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) client %C connected from %d\n"), + cli_addr.get_host_name (), + cli_addr.get_port_number ())); + + // hdr bufSize is hardcoded to 8 bytes + // (4 for a CDR-encoded boolean and 4 for a CDR-encoded ULong) + ACE_CDR::ULong hdrBufSize = 8; + // allocate a raw buffer large enough to receive the header and be + // properly aligned for the CDR decoding. + ACE_CDR::Char * hdrBuf= new ACE_CDR::Char[hdrBufSize+ACE_CDR::MAX_ALIGNMENT]; + // align the raw buffer before reading data into it. + char * hdrBuf_a = ACE_ptr_align_binary(hdrBuf, ACE_CDR::MAX_ALIGNMENT); + + size_t bt; + // read the header + if ((dataModeStream->recv_n(hdrBuf_a, hdrBufSize, 0, &bt)) == -1){ + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("recv_n"))); + return; + } + + // pass the CDR encoded data into an ACE_InputCDR class. hdrCDR does + // NOT copy this data. Nor does it delete. It assumes the buffer + // remains valid while it is in scope. + ACE_InputCDR hdrCDR(hdrBuf_a, hdrBufSize); + + ACE_CDR::Boolean byteOrder; + ACE_CDR::ULong numIterations; + + // extract the data + hdrCDR >> ACE_InputCDR::to_boolean (byteOrder); + hdrCDR.reset_byte_order(byteOrder); + hdrCDR >> numIterations; + + // make sure the stream is good after the extractions + if (!hdrCDR.good_bit()){ + ACE_ERROR((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("hdrCDR"))); + + return; + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Test for %u iterations\n"), + numIterations)); + + // deallocate the header buffer + delete[] hdrBuf; + + // bundle up the arguments + ArgStruct * args = new ArgStruct; + args->stream = dataModeStream; + args->numIters = numIterations; + +#if defined (ACE_HAS_THREADS) + // Spawn a new thread and run the new connection in that thread of + // control using the <server> function as the entry point. + if (ACE_Thread_Manager::instance ()->spawn (unmarshalledOctetServer, + reinterpret_cast<void *> (args), + THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("spawn"))); +#else + (*unmarshalledOctetServer) (reinterpret_cast<void *> (args)); +#endif /* ACE_HAS_THREADS */ + +} + +int ACE_TMAIN (int argc, ACE_TCHAR **argv){ + + Options_Manager optsMgr(argc, argv, ACE_TEXT ("server-opts")); + + // show usage is requested + if (optsMgr._usage) { + optsMgr._show_usage(stderr, ACE_TEXT ("server-opts")); + return 1; + } + + // If SCTP is not installed then terminate the program, unless TCP + // was specified. +#ifndef ACE_HAS_SCTP + if (optsMgr.test_transport_protocol == IPPROTO_SCTP) + ACE_ERROR_RETURN((LM_ERROR, + ACE_TEXT ("SCTP was NOT installed when this binary was compiled.\n") + ACE_TEXT ("SOCK_STREAM_srv may still be run using TCP ") + ACE_TEXT ("via the '-t tcp' option.\n")), + 1); +#endif + + // check that valid options were specified + if (optsMgr._error) { + ACE_OS::fprintf (stderr, "ERROR: %s\n", ACE_TEXT_ALWAYS_CHAR (optsMgr._error_message)); + return 1; + } + + // this is the socket that the server will listen on + ACE_SOCK_Acceptor acceptor_socket; + + // Create the address that we want to listen for connections on. If + // server_accept_addr=INADDR_ANY (the default), SCTP will listen for + // connections on all IP interfaces. If an address is specified, + // SCTP will listen for connections on that address ONLY. + ACE_INET_Addr serverAddr(optsMgr.server_port, + optsMgr.server_accept_addr); + + ACE_DEBUG((LM_DEBUG, + ACE_TEXT ("(%P|%t) Accepting connections on port %u on interface %C using %C\n"), + serverAddr.get_port_number(), + (optsMgr.server_accept_addr == INADDR_ANY) ? "INADDR_ANY" : serverAddr.get_host_addr(), + (optsMgr.test_transport_protocol == IPPROTO_SCTP) ? "IPPROTO_SCTP" : "IPPROTO_TCP")); + + // this operation creates a socket, binds the specified internet + // address to it and calls listen. As this is a wrapper facade + // approach, the ACE_OS::{socket,bind,listen} calls are invoked in + // the implementation of open. + if (acceptor_socket.open(serverAddr, 1, + AF_INET, + ACE_DEFAULT_BACKLOG, + optsMgr.test_transport_protocol) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("open")), + 1); + + // this function checks that the port that was actually bound was + // the port we asked for. Apparently some operating systems will + // automatically select new ports if the specified port is currently + // used. + else if (acceptor_socket.get_local_addr(serverAddr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("get_local_addr")), + 1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) starting server at port %d\n"), + serverAddr.get_port_number())); + + // this is the stream object that will associated with a completed + // connection (aka the data mode socket). It will be set when accept + // is called below. + ACE_SOCK_Stream new_stream; + + // a file decriptor set + ACE_Handle_Set handle_set; + // add the acceptor socket to the file descriptor set. + handle_set.set_bit(acceptor_socket.get_handle()); + + for (;;){ + + ACE_Time_Value timeout(ACE_DEFAULT_TIMEOUT); + ACE_Handle_Set temp = handle_set; + + // wait for connect() call from client. In the original test there + // were two different acceptor sockets for two different + // services. So select was needed to wait on both sockets + // simultaneously. In this test we could just call accept on the + // one socket. + int result = ACE_OS::select((int) (acceptor_socket.get_handle()) +1, + (fd_set *) temp, + 0, + 0, + timeout); + + // check that select did not end with an error. + if (result == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select"))); + // check to see if select timed out. + else if (result == 0){ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) select timed out\n"))); + + } + else { // case where a file descriptor was actually set + if (!(temp.is_set(acceptor_socket.get_handle()))){ + // CANNOT BE REACHED + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%P|%t) %p\n"), + ACE_TEXT ("select: NO ERROR BUT NO FD SET"))); + } else { + // call accept to set up the new stream. + if (acceptor_socket.accept(new_stream) == -1) { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("accept"))); + continue; + } + else{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) spawning server\n"))); + + } + // Run the server. + run_server (new_stream.get_handle ()); + } + + } + + } + ACE_NOTREACHED (return 0;) +} diff --git a/ACE/performance-tests/SCTP/THANKS b/ACE/performance-tests/SCTP/THANKS new file mode 100644 index 00000000000..eb646d9dae1 --- /dev/null +++ b/ACE/performance-tests/SCTP/THANKS @@ -0,0 +1,30 @@ +Lockheed Martin Advanced Technology Laboratories (LM ATL) development +of the SCIOP pluggable protocol for TAO was funded under DARPA/IXO's +Program Composition for Embedded Systems (PCES) program. + +The LM ATL PCES project leads, Patrick Lardieri and Gautam Thaker, +thank the following individuals for their contributions to the +development TAO's SCIOP pluggable protocol. + +Jason Cohen LM ATL +Edward Mulholland LM ATL +Keith O'Hara LM ATL +Chuck Winters LM ATL + +We also thank Mr. Brian Bidulock of OpenSS7 (www.openss7.org) for his +rapid development of a fully functional Linux implementation of SCTP +which made this work possible. + +We also thank the following individuals for their valuable review and +critiques of our SCIOP designs and for the expert advice on the more +arcane and subtle aspects of ACE and TAO. + +Dr. Chris Gill DOC Group +Balachandran Natarajan DOC Group + +Dr. Douglas Schmidt DARPA/IXO, DOC Group + +Steve Houston OCI + + + diff --git a/ACE/performance-tests/SCTP/hist.cpp b/ACE/performance-tests/SCTP/hist.cpp new file mode 100644 index 00000000000..c67916a86cd --- /dev/null +++ b/ACE/performance-tests/SCTP/hist.cpp @@ -0,0 +1,312 @@ +// $Id$ + +/* Replacement for CSIM histogram created by Alex Carobus 7/20/98 + #include <std/disclaimer.h> + */ +/* +// +// Update history: +// +// [99.10.18] jcohen +// version 1.3 +// added optional fields to header +// to use: add_field("key","value",histogram); +// or: add_field_n("key",45,histogram); +// +// [99.01.08] jcohen +// version 1.2 +// histogram includes skew and kurtosis +// call enable_skew(h) to enable skew and kurtosis on histogram h +// +// [98.07.29] jcohen +// added add_histogram function +// added version 1.1 display, added num_bins display +// added read_hist +// +*/ + +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_stdlib.h" +#include <float.h> +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_math.h" +#include "hist.h" +namespace ACE_SCTP +{ + +hist_t *head_hist, *tail_hist; + +hist_t *histogram(char *name, unsigned int num_bins, double first, + double last) { + ACE_SCTP::hist_t *hist; + if ((hist = (hist_t *)ACE_OS::malloc(sizeof(hist_t))) == 0) { + ACE_OS::fprintf(stderr, "unable to allocate memory for histogram : %s", name); + ACE_OS::exit(-1); + } + if ((hist->hs = (unsigned int *)ACE_OS::malloc(sizeof(unsigned int) * (num_bins+2))) == 0){ + ACE_OS::fprintf(stderr, "unable to allocate memory for histogram : %s", name); + ACE_OS::exit(-1); + } + ACE_OS::memset(hist->hs, 0, sizeof(unsigned int) * (num_bins+2)); + hist->name = name; + hist->num_bins = num_bins; + hist->first = first; + hist->last = last; + hist->num_points = 0; + hist->max_num_outer = hist->num_outer = 0; + hist->sum = 0.0; + hist->sum2 = 0.0; + hist->sum3 = 0.0; + hist->sum4 = 0.0; + hist->max = DBL_MIN; + hist->min = DBL_MAX; + hist->outer = 0; + hist->next = 0; + hist->skew = 0; + hist->firstoptheader = 0; + if(head_hist == 0) + head_hist = tail_hist = hist; + else { + tail_hist->next = hist; + tail_hist = tail_hist->next; + } + return hist; +} + + +void set_outer(unsigned int max_num_outer, hist_t *hist) { + hist->outer = (double *)ACE_OS::realloc(hist->outer,sizeof(double) * max_num_outer); + hist->max_num_outer = max_num_outer; +} + + +void enable_skew(hist_t *hist) { + hist->skew = 1; +} + + +void add_field(char *key, char *value, hist_t *hist) { + struct optheader *nextoptheader, *trace; + + /* create and prepare nextoptheader */ + nextoptheader = (struct optheader *) ACE_OS::malloc(sizeof(struct optheader)); + nextoptheader->key = (char *) ACE_OS::malloc(ACE_OS::strlen(key)+1); + nextoptheader->value = (char *) ACE_OS::malloc(ACE_OS::strlen(value)+1); + nextoptheader->next = 0; + ACE_OS::strcpy(nextoptheader->key,key); + ACE_OS::strcpy(nextoptheader->value,value); + + /* tack nextoptheader onto end of optheader list */ + if (hist->firstoptheader == 0) { + hist->firstoptheader = nextoptheader; + } else { + trace = hist->firstoptheader; + while (trace->next != 0) { + trace = trace->next; + } + trace->next = nextoptheader; + } +} + + +void add_field_n(char *key, int value, hist_t *hist) { + char textvalue[40]; + + ACE_OS::sprintf(textvalue,"%d",value); + add_field(key,textvalue,hist); +} + + +void record(double point, hist_t *hist) { + unsigned int bin; + double incsum; + if (point < hist->min) hist->min = point; + if (point > hist->max) hist->max = point; + if (point < hist->first) { + bin = 0; + if (hist->max_num_outer > hist->num_outer) + hist->outer[hist->num_outer++] = point; + } + else if (point >= hist->last) { + bin = hist->num_bins+1; + if (hist->max_num_outer > hist->num_outer) + hist->outer[hist->num_outer++] = point; + } + else + bin = (unsigned int)(((point - hist->first)*(hist->num_bins))/(hist->last - hist->first)+1); + + hist->hs[bin]++; + hist->num_points++; + hist->sum+=point; + incsum = point*point; + hist->sum2 += incsum; + if (hist->skew) { + incsum *= point; + hist->sum3 += incsum; + incsum *= point; + hist->sum4 += incsum; + } + +/* printf("point : %g, bin %u, points in bin %u\n", point, bin, hist->hs[bin]); */ +} + +void report_to(FILE *strm, hist_t *hist) { + unsigned int sofar, i; + double p1, p2, d, mean; + double variance; + struct optheader *trace; + + if (hist->num_points == 0) { + ACE_OS::fprintf(strm, "\n\n\t\t\t\tHistogram %s is empty\n\n", hist->name); + return; + } + ACE_OS::fprintf(strm, "\n\n\t\t\t\tHistogram %s\n", hist->name); + if (hist->skew) ACE_OS::fprintf(strm, "version: %s\n", HIST_VERSION); + else ACE_OS::fprintf(strm, "version: 1.1\n"); + ACE_OS::fprintf(strm, "minimum: %g\n", hist->min); + ACE_OS::fprintf(strm, "maximum: %g\n", hist->max); + ACE_OS::fprintf(strm, "mean: %g\n", mean = hist->sum/hist->num_points); + variance = (hist->sum2 - + 2*hist->sum*mean + + hist->num_points*mean*mean) / (hist->num_points-1); + ACE_OS::fprintf(strm, "variance: %g\n", variance); + if (hist->skew) + { + ACE_OS::fprintf(strm, "skew: %g\n", ((hist->sum3 - + 3*hist->sum2*mean + + 3*hist->sum*mean*mean - + hist->num_points*mean*mean*mean) / + pow(sqrt(variance),3) / hist->num_points)); + + ACE_OS::fprintf(strm, "kurtosis: %g\n", ((hist->sum4 - + 4*hist->sum3*mean + + 6*hist->sum2*mean*mean - + 4*hist->sum*mean*mean*mean + + hist->num_points*mean*mean*mean*mean) / + pow(sqrt(variance),4)) / hist->num_points - 3); + } + ACE_OS::fprintf(strm, "num_points: %u\n", hist->num_points); + ACE_OS::fprintf(strm, "num_bins: %d %g %g\n", hist->num_bins,hist->first,hist->last); + if (hist->firstoptheader) { + trace = hist->firstoptheader; + while(trace->next != 0) + { + ACE_OS::fprintf(strm, "%s: %s\n", trace->key, trace->value); + trace = trace->next; + } + ACE_OS::fprintf(strm, "%s: %s\n", trace->key, trace->value); + } + ACE_OS::fprintf(strm, "\n"); + sofar = hist->hs[0]; + ACE_OS::fprintf(strm, "\t Low - High Count Fraction Cumulative\n"); + ACE_OS::fprintf(strm, "\t below - %12.3f : %5u %0.3f %0.3f\n", hist->first, + hist->hs[0], (double)hist->hs[0]/hist->num_points, + (double)histfloor(1000.0*sofar/hist->num_points)/1000.0); + p2 = hist->first; + d = (hist->last - hist->first)/hist->num_bins; + for(i = 1; i <= hist->num_bins; i++) { + p1 = p2; + p2 = i*d+hist->first; + sofar += hist->hs[i]; + ACE_OS::fprintf(strm, "\t%12.3f - %12.3f : %5u %0.3f %0.3f\n", p1, p2, hist->hs[i], + (double)hist->hs[i]/hist->num_points, + (double)histfloor(1000.0*sofar/hist->num_points)/1000.0); + } + + sofar += hist->hs[hist->num_bins+1]; + ACE_OS::fprintf(strm, "\t%12.3f - above : %5u %0.3f %0.3f\n\n", + hist->last, hist->hs[hist->num_bins+1], + (double)hist->hs[hist->num_bins+1]/hist->num_points, + (double)histfloor(1000.0*sofar/hist->num_points)/1000.0); + + if (hist->num_outer) + { + ACE_OS::fprintf(strm, "outliers:\n"); + for(i = 0; i < hist->num_outer; i++) ACE_OS::fprintf(strm, "\t%12.3f\n", + hist->outer[i]); + } + ACE_OS::fprintf(strm, "\n\n"); +} + +void report() { + hist_t *temp = head_hist; + for(; temp; temp = temp->next) + report_to(stdout, temp); +} + +void stats_init() { + /* this is not necessary */ +} + +double get_mean(HIST hist) { + return(hist->sum/hist->num_points); +} + +double get_min(HIST hist) { + return(hist->min); +} + +double get_max(HIST hist) { + return(hist->max); +} + +double get_variance(HIST hist) { + double mean = hist->sum/hist->num_points; + return((hist->sum2 - 2*hist->sum*mean + hist->num_points*mean*mean)/ + (hist->num_points-1)); +} + +double get_num(HIST hist) { + return(hist->num_points); +} + +void set_mean(HIST hist, double mean) { + hist->sum = mean*hist->num_points; +} + +void set_min(HIST hist, double min) { + hist->min = min; +} + +void set_max(HIST hist, double max) { + hist->max = max; +} + +void set_variance(HIST hist, double variance) { + /* do this after seting mean */ + double mean = hist->sum/hist->num_points; + hist->sum2 = variance *(hist->num_points-1) + 2*hist->sum*mean - hist->num_points*mean*mean; +} + +void add_histogram(HIST dest, HIST source) { + unsigned int i,j; + double d,midpoint; + + d = (source->last - source->first)/source->num_bins; + + for(i=1;i<=source->num_bins;i++) { + midpoint = source->first + ((i - 0.5) * d); + for(j=0;j<source->hs[i];j++) { + record(midpoint,dest); + } + } + + for(i=0;i<source->num_outer;i++) + record(source->outer[i],dest); + + set_min(dest, get_min(source)); + set_max(dest, get_max(source)); + set_mean(dest, get_mean(source)); + set_variance(dest, get_variance(source)); +} + +double histfloor (double x) { + +#ifdef WIN32 + return ACE_OS::floor(x); +#else + return static_cast<double> (static_cast<long long> (x)); +#endif + +} +} diff --git a/ACE/performance-tests/SCTP/hist.h b/ACE/performance-tests/SCTP/hist.h new file mode 100644 index 00000000000..efb83716267 --- /dev/null +++ b/ACE/performance-tests/SCTP/hist.h @@ -0,0 +1,71 @@ +// $Id$ + +#include <stdio.h> + +#define HIST_VERSION "1.3" + +namespace ACE_SCTP +{ + + struct optheader { + char *key; + char *value; + struct optheader *next; + }; + + typedef struct hist_s { + char *name; + double max, min, sum, sum2, sum3, sum4; + double first, last; + unsigned int num_points, num_bins; + unsigned int *hs; + unsigned int num_outer, max_num_outer; + double *outer; + struct hist_s *next; + int skew; + struct optheader *firstoptheader; + } hist_t; + + #define HIST hist_t * + + hist_t *histogram(char *name, unsigned int num_bins, double first, + double last); + + void set_outer(unsigned int max_num_outer, hist_t *hist); + + void enable_skew(hist_t *hist); + + void add_field(char *key, char *value, hist_t *hist); + + void add_field_n(char *key, int value, hist_t *hist); + + void record(double point, hist_t *hist); + + void report_to(FILE *strm, hist_t *hist); + + void report(); + + void stats_init(); + + double get_mean(HIST hist); + + double get_min(HIST hist); + + double get_max(HIST hist); + + double get_variance(HIST hist); + + double get_num(HIST hist); + + void set_mean(HIST hist, double mean); + + void set_min(HIST hist, double min); + + void set_max(HIST hist, double max); + + void set_variance(HIST hist, double variance); + + void add_histogram(HIST dest, HIST source); + + double histfloor(double x); +} diff --git a/ACE/performance-tests/SCTP/run_spectrum.config b/ACE/performance-tests/SCTP/run_spectrum.config new file mode 100644 index 00000000000..cb2beb17f5e --- /dev/null +++ b/ACE/performance-tests/SCTP/run_spectrum.config @@ -0,0 +1,12 @@ +# +# $Id$ +# +# This is the default configuration file for run_spectrum.pl. +# Configuration options given in this file will be passed to +# SOCK_STREAM_clt or SOCK_SEQPACK_clt. +# +# Note that the configuration options are defined as "the first +# double-quote string that is not to the right of a pound sign." +# (Double-quotes and pound signs cannot be escaped). + +"-c 1000" diff --git a/ACE/performance-tests/SCTP/run_spectrum.pl b/ACE/performance-tests/SCTP/run_spectrum.pl new file mode 100755 index 00000000000..d6ec34e6075 --- /dev/null +++ b/ACE/performance-tests/SCTP/run_spectrum.pl @@ -0,0 +1,260 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ + +use warnings; +use strict; + +use lib '../../bin'; +use PerlACE::Run_Test; +use Getopt::Long; +use Pod::Usage; + +#### Program Documentation #### + +=head1 NAME + +run_spectrum.pl - Run a spectrum of performance tests. + +=head1 SYNOPSIS + + run_spectrum.pl [ --help ] + [ --manual ] + [ --service (STREAM|SEQPACK) ] + [ --protocol (TCP|SCTP) ] + [ --config_file filename ] + +=head1 OPTIONS + +=over 8 + +=item B<--help, -h> + +Print this help message and exit. + +=item B<--manual, -m> + +Print full documentation and exit. + +=item B<--service, -s> (STREAM|SEQPACK) + +Select SOCK_STREAM or SOCK_SEQPACK service respectively. The default +is SOCK_STREAM. + +=item B<--protocol, -p> (TCP|SCTP) + +Select TCP or SCTP transport protocol. The default is TCP. Note that +TCP cannot be used together with the SEQPACK service. + +=item B<--config_file, -c> filename + +Read configuration parameters from the nominated file. By default, +parameters are read from C<run_spectrum.config> in the current +directory. + +=back + +=head1 DESCRIPTION + +This directory contains performance tests for measuring round-trip +latency statistics of ACE synchronous messaging using unmarshalled +ACE_CDR::Octet. + +The SOCK_STREAM performance test comprises SOCK_STREAM_clt on the +client side and SOCK_STREAM_srv on the server side. The SOCK_SEQPACK +performance test comprises SOCK_SEQPACK_clt on the client side and +SOCK_SEQPACK_srv on the server side. + +People who are interested in round-trip latency often want to see a +I<spectrum> of statistics for a range of payload sizes. The job of +this script, B<run_spectrum.pl>, is to run the client-side +performance-test program repeatedly, so as to generate a spectrum of +statistics. + +The script supports two types of services (SOCK_STREAM and +SOCK_SEQPACK) and two protocols (TCP and UDP). All combinations are +valid except TCP over SOCK_SEQPACK. Please see L<"OPTIONS"> for +information about the command-line options that specify the service +and the protocol. + +By default, B<run_spectrum.pl> will execute SOCK_STREAM_clt (or +SOCK_SEQPACK_clt) with a minimal set of command-line options. You can +specify additional options in an external configuration file. By +default, the configuration file is called C<run_spectrum.config>. You +may name an alternate configuration file using the B<--config_file> +option. + +The configuration file should contain the options that you would +ordinarly give to SOCK_STREAM_clt or SOCK_SEQPACK_clt on the command +line. To see the command-line options available for these programs, +please run C<SOCK_STREAM_clt --help> or C<SOCK_SEQPACK_clt --help>. +Note that the options B<-s> and B<-t> are used by this script when it +launches the client. An occurrence of either of these options in the +configuration file will be ignored as redundant. + +The client is executed 15 times: once for each payload size from 2^2 +bytes to 2^16 bytes. (SCTP tests only go up to 2^15 bytes because of +limitations in our SCTP implementation.) Histograms summarizing the +round-trip latency performance are dumped to a file called +C<TIMESTAMP.spectrum> where C<TIMESTAMP> is the current time in +hyphen-delimited form. + +=head1 EXAMPLES + +B<Example 1>: Run a spectrum of TCP performance tests using +SOCK_STREAM with both endpoints on the local machine. Each test in +the spectrum should have 10,000 data points. + +=over 4 + +=item 1. + +Start the SOCK_STREAM server on the local machine with the S<C<-t +tcp>> option: + + % ./SOCK_STREAM_srv -t tcp + +=item 2. + +Configure the client to run 10,000 individual trials: + + % echo '"-c 10000"' > run_spectrum.config + +=item 3. + +Execute B<run_spectrum.pl>: + + % ./run_spectrum.pl + +(The script uses TCP and SOCK_STREAM by default.) + +=back + +B<Example 2>: Run a spectrum of SCTP performance tests using +SOCK_SEQPACK with one endpoint on the local machine and one endpoint +on a remote machine that happens to be named oneida. Each test in the +spectrum should have 50,000 data points. + +=over 4 + +=item 1. + +On the remote host oneida, start the SOCK_SEQPACK server: + + % ./SOCK_SEQPACK_srv + +=item 2. + +On the local host, configure the client to run 50,000 individual +trials and to look for the server on oneida: + + % echo '"-c 50000 -H oneida"' > run_spectrum.config + +=item 3. + +On the local host, execute B<run_spectrum.pl>: + + % ./run_spectrum.pl --service SEQPACK --protocol SCTP + +=back + +=cut + +# Global variable declarations +my $help = 0; +my $manual = 0; +my $service = "stream"; +my $protocol = "tcp"; +my $config_file = "run_spectrum.config"; +my $options = ""; +my $output_file; + +# Map from services to client programs +my %client_programs = (stream => "SOCK_STREAM_clt", + seqpack => "SOCK_SEQPACK_clt"); + +#### MAIN PROGRAM #### + +# Canonicalize arguments to lower-case +tr/A-Z/a-z/ for @ARGV; + +# Use Getopt::Long to parse options +GetOptions ("help" => \$help, + "manual" => \$manual, + "service=s" => \$service, + "protocol=s" => \$protocol, + "config_file=s" => \$config_file + ); + +# Show help if requested +pod2usage(1) if $help; + +# Show manual if requested +pod2usage(-verbose => 2) if $manual; + +# Validate service option +$service =~ s/seqpacket/seqpack/; # Accept "seqpacket" as synonym for "seqpack" +$service =~ /^(stream|seqpack)$/ + or pod2usage("Service must be STREAM or SEQPACK\n"); + +$protocol =~ /^(tcp|sctp)$/ + or pod2usage("Protocol must be TCP or SCTP\n"); + +$service eq "seqpack" and $protocol eq "tcp" + and pod2usage("Cannot use SEQPACK service with TCP\n"); + +# Open config file +open CONFIG, "< $config_file" + or die "cannot open file: $config_file\n"; + +# Read options from the config file. The options are defined as the +# first double-quoted string that is not to the right of a pound sign. +# (Pound signs and quotes cannot be escaped.) + +while (<CONFIG>) { + chomp; + if (/^[^\#]*?\"(.*?)\"/) { + $options = $1; + last; + } +} + +# Close config file +close CONFIG; + +# Print options +print "Read options from $config_file: \"$options\"\n\n"; + +# Name a unique file to store the results in. +($output_file = localtime() . ".spectrum") =~ tr/ /-/; + +# Run the test for message sizes ranging from 2^2 bytes to 2^16 bytes +# (SCTP tests only go up to 2^15 bytes because of limitations in our +# SCTP implementation) +my $max_size = 16; +$max_size = 15 if $protocol eq "sctp"; +for (my $i = 2; $i <= $max_size; ++$i) { + + # Assemble client parameters and print out the command line + my $client_params = "-t $protocol -s $i $options >> $output_file"; + print "$client_programs{$service} $client_params\n"; + + # Configure client + my $client = + new PerlACE::Process($client_programs{$service}, $client_params); + + # Spawn client + $client->Spawn(); + + # Wait for client to finish + $client->Wait(); + + # Remind the client that it's finished running + $client->{RUNNING} = 0; + + # Sleep for a while + sleep 5; +} + diff --git a/ACE/performance-tests/SCTP/sample-spectrum.png b/ACE/performance-tests/SCTP/sample-spectrum.png Binary files differnew file mode 100644 index 00000000000..d6fc94f205b --- /dev/null +++ b/ACE/performance-tests/SCTP/sample-spectrum.png diff --git a/ACE/performance-tests/Server_Concurrency/Latency_Stats.h b/ACE/performance-tests/Server_Concurrency/Latency_Stats.h new file mode 100644 index 00000000000..2b0694b7ea7 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Latency_Stats.h @@ -0,0 +1,219 @@ +// $Id$ + +class Latency_Stats +{ +public: + Latency_Stats (void); + + void dump_results (const ACE_TCHAR* test_name, + const ACE_TCHAR* sub_test); + + void sample (ACE_hrtime_t sample); + + void accumulate (const Latency_Stats& stats); + // Useful to merge several Latency_Stats. + +private: + u_long n_; + ACE_hrtime_t sum_; + ACE_hrtime_t sum2_; + ACE_hrtime_t min_; + ACE_hrtime_t max_; +}; + +inline +Latency_Stats::Latency_Stats (void) + : n_ (0), + sum_ (0), + sum2_ (0), + min_ (0), + max_ (0) +{ +} + +inline void +Latency_Stats::sample (ACE_hrtime_t sample) +{ + this->sum_ += sample; +#ifndef ACE_LACKS_LONGLONG_T + this->sum2_ += sample * sample; +#else + // possible loss of precision here due to lack of 64bit support + this->sum2_ += sample * sample.lo(); +#endif + if (this->n_ == 0) + { + this->min_ = sample; + this->max_ = sample; + } + if (this->min_ > sample) + this->min_ = sample; + if (this->max_ < sample) + this->max_ = sample; + this->n_++; +} + +inline void +Latency_Stats::dump_results (const ACE_TCHAR *test_name, + const ACE_TCHAR *sub_test) +{ + if (this->n_ < 1) + return; + + ACE_hrtime_t avg = this->sum_ / this->n_; +#ifndef ACE_LACKS_LONGLONG_T + ACE_hrtime_t dev = + this->sum2_ / this->n_ - avg*avg; +#else + ACE_hrtime_t dev = + this->sum2_ / this->n_ - avg.lo()*avg.lo(); +#endif + ACE_UINT32 gsf = ACE_High_Res_Timer::global_scale_factor (); + + double min_usec = ACE_CU64_TO_CU32 (this->min_) / gsf; + double max_usec = ACE_CU64_TO_CU32 (this->max_) / gsf; + double avg_usec = ACE_CU64_TO_CU32 (avg) / gsf; + double dev_usec = ACE_CU64_TO_CU32 (dev) / (gsf * gsf); + ACE_DEBUG ((LM_DEBUG, + "%s/%s: %.2f/%.2f/%.2f/%.2f (min/avg/max/var^2) [usecs]\n", + test_name, sub_test, + min_usec, avg_usec, max_usec, dev_usec)); +} + +inline void +Latency_Stats::accumulate (const Latency_Stats& rhs) +{ + if (rhs.n_ == 0) + return; + + if (this->n_ == 0) + { + this->n_ = rhs.n_; + this->min_ = rhs.min_; + this->max_ = rhs.max_; + this->sum_ = rhs.sum_; + this->sum2_ = rhs.sum2_; + return; + } + + if (this->min_ > rhs.min_) + this->min_ = rhs.min_; + if (this->max_ < rhs.max_) + this->max_ = rhs.max_; + + this->sum_ += rhs.sum_; + this->sum2_ += rhs.sum2_; + this->n_ += rhs.n_; +} + +class Throughput_Stats +{ +public: + Throughput_Stats (void); + + void dump_results (const ACE_TCHAR* test_name, + const ACE_TCHAR* sub_test); + + void sample (void); + // An event has been received + + void accumulate (const Throughput_Stats& stats); + // Useful to merge several Throughput_Stats. + +private: + u_long n_; + ACE_hrtime_t start_; + ACE_hrtime_t stop_; +}; + +inline void +Throughput_Stats::accumulate (const Throughput_Stats& rhs) +{ + if (rhs.n_ == 0) + return; + + if (this->n_ == 0) + { + this->start_ = rhs.start_; + this->stop_ = rhs.stop_; + this->n_ = rhs.n_; + return; + } + + if (this->start_ > rhs.start_) + this->start_ = rhs.start_; + + if (this->stop_ < rhs.stop_) + this->stop_ = rhs.stop_; + + this->n_ += rhs.n_; +} + +inline void +Throughput_Stats::dump_results (const ACE_TCHAR *test_name, + const ACE_TCHAR *subtest) +{ + if (this->n_ == 0) + { + ACE_DEBUG ((LM_DEBUG, + "%s/%s: no events recorded\n", + test_name, subtest)); + return; + } + + ACE_Time_Value tv; + ACE_High_Res_Timer::hrtime_to_tv (tv, this->stop_ - this->start_); + + double f = 1.0/(tv.sec () + tv.usec () / 1000000.0); + double events_per_second = this->n_ * f; + + ACE_DEBUG ((LM_DEBUG, + "%s/%s: " + "%d / %d.%06.6d = %.3f events/second\n", + test_name, subtest, + this->n_, + tv.sec (), tv.usec (), + events_per_second)); +} + +inline +Throughput_Stats::Throughput_Stats (void) + : n_ (0), + start_ (), + stop_ () +{ +} + +inline void +Throughput_Stats::sample (void) +{ + if (this->n_ == 0) + { + this->start_ = ACE_OS::gethrtime (); + } + this->n_++; + this->stop_ = ACE_OS::gethrtime (); +} + +inline void +move_to_rt_class (void) +{ + // Enable FIFO scheduling, e.g., RT scheduling class on Solaris. + int priority = + (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO) + + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO)) / 2; + + int result = ACE_OS::sched_params (ACE_Sched_Params (ACE_SCHED_FIFO, + priority, + ACE_SCOPE_PROCESS)); + if (result == 0) + { + result = ACE_OS::thr_setprio (priority); + } + + if (result != 0) + { + ACE_DEBUG ((LM_DEBUG, + "Cannot move program to realtime class.\n")); + } +} diff --git a/ACE/performance-tests/Server_Concurrency/Leader_Follower/Makefile.am b/ACE/performance-tests/Server_Concurrency/Leader_Follower/Makefile.am new file mode 100644 index 00000000000..f4369f7ad34 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Leader_Follower/Makefile.am @@ -0,0 +1,60 @@ +## 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.Svr_Conc_Leader_Follower.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += leader_follower + +leader_follower_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +leader_follower_SOURCES = \ + leader_follower.cpp \ + RT_CORBA_Leader_Follower.h + +leader_follower_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Svr_Conc_Leader_Follower_RT_CORBA.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += RT_CORBA_Leader_Follower + +RT_CORBA_Leader_Follower_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +RT_CORBA_Leader_Follower_SOURCES = \ + RT_CORBA_Leader_Follower.cpp \ + RT_CORBA_Leader_Follower.h + +RT_CORBA_Leader_Follower_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/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.cpp b/ACE/performance-tests/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.cpp new file mode 100644 index 00000000000..d31b85d90b2 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.cpp @@ -0,0 +1,401 @@ +// $Id$ + +#include "RT_CORBA_Leader_Follower.h" + + + +#if defined (ACE_HAS_THREADS) + +// We need the following only if we have threads enabled.. +#include "ace/OS_main.h" +#include "ace/ACE.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" +#include "ace/Profile_Timer.h" + +// Number of messages that are used in this experiment +static size_t number_of_messages = 1000; + +// Number of messages that are used in this experiment +static size_t number_of_messages_left = 0; + +// Number of threads used in this experiment +static size_t number_of_threads = 2; + +// Global variable for the availability of the leader +static size_t leader_available = 0; + +// Number of threads that are ready to go/dispatch +static size_t ready_threads = 0; + +// Work in the upcall +static size_t message_size = 100; + +// Debugging condition +static DEBUGGING_RANGE debug = DEBUG_NONE; + +// Timer for the test +ACE_High_Res_Timer test_timer; + + + + +/*******************************************************************/ +// Constructor for Synchronisers +Synchronisers::Synchronisers (void) + : mutex_ (), + condition_ (mutex_), + event_ () +{ +} + + +int +Synchronisers::start_synchronization (void) +{ + // Hold the lock and increment the global variable to indicate + // number of ready threads + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + ready_threads ++; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Ready to go.. \n")); + } + + if (ready_threads == number_of_threads) + { + + // Reset the ready_threads so that we can wait at the end of + // runs + ready_threads = 0; + + // Start the timer + test_timer.start (); + + // Signal all the threads + this->event_.signal (); + + // return to do our work; + return 0; + } + + // If we are not the last thread, let go off the lock + } + + // Wait blisfully till we are woken up + this->event_.wait (); + + return 0; +} + +int +Synchronisers::end_synchronization (void) +{ + // Hold the lock and increment the global variable to indicate + // number of ready threads + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + ready_threads ++; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Ready to go.. \n")); + } + + if (ready_threads == number_of_threads) + { + + // Reset the ready_threads so that we can wait at the end of + // runs + ready_threads = 0; + + // Start the timer + test_timer.stop (); + + // Signal all the threads + this->event_.signal (); + + // return to do our work; + return 0; + } + + // If we are not the last thread, let go off the lock + } + + // Wait blisfully till we are woken up + this->event_.wait (); + + return 0; +} + + +/*******************************************************************/ + +Leader_Follower_Task::Leader_Follower_Task (Synchronisers &synch) + : messages_consumed_ (0), + synch_ (synch) +{ +} + +int +Leader_Follower_Task::processed (void) +{ + return this->messages_consumed_; +} + +int +Leader_Follower_Task::svc (void) +{ + (void) this->synch_.start_synchronization (); + + for (;;) + { + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->synch_.mutex_, -1); + + // Wait until there is no leader. + while (leader_available) + { + int result = this->synch_.condition_.wait (); + + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Leader_Follower_Task::svc (%t) -> %p\n", + "wait error"), + -1); + } + } + + // I am the leader. + leader_available = 1; + + // + // We are letting go of the leader follower lock before going + // in the event loop. + // + } + + // + // It is ok to modify these shared variables without a lock + // since we are the only leader. + // + + int exit_loop = 0; + if (number_of_messages_left == 0) + { + exit_loop = 1; + } + else + { + --number_of_messages_left; + + // Local counter. + ++this->messages_consumed_; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) message for this thread %d\n", + this->messages_consumed_)); + } + } + + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->synch_.mutex_, -1); + + // I am no longer the leader. + leader_available = 0; + + // Wake up a follower. + this->synch_.condition_.signal (); + } + + if (exit_loop) + { + break; + } + else + { + // + // Process message here. + // + for (size_t j = 0; j < message_size; ++j) + { + // Eat a little CPU + /* takes about 40.2 usecs on a 167 MHz Ultra2 */ + u_long n = 11UL; + ACE::is_prime (n, 2, n / 2); + } + + } + } + + (void) this->synch_.end_synchronization (); + + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("m:t:d:s:")); + int c; + + while ((c = get_opt ()) != -1) + { + switch (c) + { + case 'm': + number_of_messages = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + number_of_threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = static_cast<DEBUGGING_RANGE> (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 's': + message_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s\n" + "\t[-m number of messages]\n" + "\t[-s message size]\n" + "\t[-w number of threads]\n" + "\t[-b burst size]\n" + "\t[-t timeout between bursts]\n" + "\t[-d debug]\n", + argv[0]), + -1); + } + } + + return 0; +} + +/*******************************************************************/ + +// Entry point + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int result = parse_args (argc, argv); + + if (result != 0) + { + return result; + } + + Synchronisers synch_forms; + + ACE_High_Res_Timer::calibrate (); + + // Leader Followers. + Leader_Follower_Task **leader_followers = 0; + ACE_NEW_RETURN (leader_followers, + Leader_Follower_Task *[number_of_threads], + -1); + + int priority = + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO); + + long flags = THR_SCOPE_PROCESS; + + // Number of messages left = Number_Of_messages + number_of_messages_left = number_of_messages; + + size_t i = 0; + // Create and activate them. + for (i = 0; i < number_of_threads; ++i) + { + ACE_NEW_RETURN (leader_followers[i], + Leader_Follower_Task (synch_forms), + -1); + + // Activate the leader_followers. + result = leader_followers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) - Activate failed for RT class " + " - Using default priority for thread [%d]\n", + i)); + + flags = THR_BOUND; + priority = ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + + // Activate the leader_followers. + result = leader_followers[i]->activate (flags, + 1, + 1, + priority); + + if (result != 0) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) - Failed again no hope \n")); + + return 0; + } + + } + } + + // Wait for all threads to terminate. + result = ACE_Thread_Manager::instance ()->wait (); + + ACE_hrtime_t elapsed_time = 0; + + test_timer.elapsed_time (elapsed_time); + + double elapsed_time_per_invocation = + static_cast<double> ( + ACE_UINT64_DBLCAST_ADAPTER (elapsed_time / number_of_messages) + ); + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Throughput is [%f] \n", + 1000000000/ elapsed_time_per_invocation)); + + + for (i = 0; i < number_of_threads; ++i) + { + ACE_DEBUG ((LM_DEBUG, + "Message consumed in thread [%d] is [%d] \n", + i, leader_followers[i]->processed ())); + delete leader_followers[i]; + } + + delete[] leader_followers; + + return result; +} + + + + +#else /*if defined (ACE_HAS_THREADS)*/ + +int +ACE_TMAIN(int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "(%p|%t) Cannot run in SIngle threaded mode \n")); + + return 0; +} +#endif /*ACE_HAS_THREADS*/ diff --git a/ACE/performance-tests/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.h b/ACE/performance-tests/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.h new file mode 100644 index 00000000000..4664bd3cda8 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Leader_Follower/RT_CORBA_Leader_Follower.h @@ -0,0 +1,109 @@ +//============================================================================= +/** + * @file RT_CORBA_Leader_Follower.h + * + * $Id$ + * + * The leader follower test modified to suit RTCORBA tests. The + * original leader follower test has too many things that doesnt + * get what we are looking for (read it as just confusing enough) + * . Hence a new test. + * + * @author Balachandran Natarajan <bala@cs.wustl.edu> + */ +//============================================================================= +#ifndef PERF_TEST_RT_CORBA_LEADER_FOLLOWER_H +#define PERF_TEST_RT_CORBA_LEADER_FOLLOWER_H +#include /**/ "ace/pre.h" +#include "ace/Task.h" + + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_HAS_THREADS) + +#include "ace/Manual_Event.h" + +enum DEBUGGING_RANGE +{ + DEBUG_NONE = 0, + DEBUG +}; + + +/** + * @class Synchronisers + * + * @brief This class provides all the synchrnoisers used in this + * test. + * + * + */ + +/// Forward declaration.. +class Leader_Follower_Task; + +class Synchronisers +{ +public: + friend class Leader_Follower_Task; + + /// Ctor + Synchronisers (void); + + int start_synchronization (void); + int end_synchronization (void); + +private: + + /// The mutex and condition variable that is used for + /// synchronisation. + ACE_SYNCH_MUTEX mutex_; + + ACE_SYNCH_CONDITION condition_; + + /// A manual event + ACE_Manual_Event event_; +}; + + +/** + * @class Leader_Follower_Task + * + * @brief This class provide the interface and an implementation to + * simulate how the LF pattern in the ORB would behave. + * + * + * Basically all threads ready to do an upcall would wait on a token + * or lock. The thread that gets the token before it gets ready to do + * the upcall would wake up a thread waiting on the token. + * + */ + +class Leader_Follower_Task : public ACE_Task_Base +{ +public: + + /// Ctor with a mutex and a condition variable + Leader_Follower_Task (Synchronisers &synch); + + /// Messages consumed + int processed (void); + + /// Method that is run by a daemon thread + int svc (void); + +private: + /// Number of messages processed by this thread + int messages_consumed_; + + /// Our reference to the synchroniser + Synchronisers &synch_; +}; + +#endif /*ACE_HAS_THREADS*/ + +#include /**/ "ace/post.h" +#endif /*PERF_TEST_RT_CORBA_LEADER_FOLLOWER*/ diff --git a/ACE/performance-tests/Server_Concurrency/Leader_Follower/Svr_Conc_Leader_Follower.mpc b/ACE/performance-tests/Server_Concurrency/Leader_Follower/Svr_Conc_Leader_Follower.mpc new file mode 100644 index 00000000000..90e800b468a --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Leader_Follower/Svr_Conc_Leader_Follower.mpc @@ -0,0 +1,18 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + avoids += ace_for_tao + exename = leader_follower + source_files { + leader_follower.cpp + } +} + +project(*RT_CORBA) : aceexe { + avoids += ace_for_tao + exename = RT_CORBA_Leader_Follower + source_files { + RT_CORBA_Leader_Follower.cpp + } +} diff --git a/ACE/performance-tests/Server_Concurrency/Leader_Follower/leader_follower.cpp b/ACE/performance-tests/Server_Concurrency/Leader_Follower/leader_follower.cpp new file mode 100644 index 00000000000..5703ff2a1af --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Leader_Follower/leader_follower.cpp @@ -0,0 +1,333 @@ +// $Id$ + +#include "ace/OS_main.h" +#include "ace/OS_NS_unistd.h" +#include "ace/ACE.h" +#include "ace/Task_T.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" +#include "ace/Profile_Timer.h" +#include "../Latency_Stats.h" + +static size_t number_of_messages = 100; +static size_t message_size = 100; +static size_t number_of_threads = 10; +static size_t burst_size = 10; +static size_t timeout_between_bursts = 1; + +static size_t leader_available = 0; +static size_t messages_in_this_burst = 0; +static size_t total_messages_consumed = 0; +static size_t burst = 1; + +static ACE_hrtime_t start_of_burst; + +enum DEBUGGING_RANGE +{ + DEBUG_NONE = 0, + DEFAULT = 1, + PRINT_INDIVIDUAL_LATENCY = 2 +}; + +static DEBUGGING_RANGE debug = DEBUG_NONE; + +typedef ACE_Task<ACE_SYNCH> TASK; + +class Leader_Follower_Task : public TASK +{ +public: + Leader_Follower_Task (ACE_SYNCH_MUTEX &mutex, + ACE_SYNCH_CONDITION &condition); + int svc (void); + + size_t messages_consumed_; + ACE_SYNCH_MUTEX &mutex_; + ACE_SYNCH_CONDITION &condition_; + + Latency_Stats latency_stats_; + Throughput_Stats throughput_stats_; +}; + +Leader_Follower_Task::Leader_Follower_Task (ACE_SYNCH_MUTEX &mutex, + ACE_SYNCH_CONDITION &condition) + : messages_consumed_ (0), + mutex_ (mutex), + condition_ (condition) +{ +} + +int +Leader_Follower_Task::svc (void) +{ + for (;;) + { + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + // Wait until there is no leader. + while (leader_available) + { + int result = this->condition_.wait (); + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Leader_Follower_Task::svc (%t) -> %p\n", + "wait error"), + -1); + } + } + + // I am the leader. + leader_available = 1; + + // + // We are letting go of the leader follower lock before going + // in the event loop. + // + } + + // + // It is ok to modify these shared variables without a lock + // since we are the only leader. + // + + int exit_loop = 0; + if (number_of_messages == 0) + { + exit_loop = 1; + } + else + { + if (messages_in_this_burst == burst_size) + { + ++burst; + messages_in_this_burst = 0; + ACE_Time_Value tv (0, timeout_between_bursts); + ACE_OS::sleep (tv); + } + + if (messages_in_this_burst == 0) + { + start_of_burst = ACE_OS::gethrtime (); + } + + --number_of_messages; + + // Burst counter. + ++messages_in_this_burst; + + // Global counter. + ++total_messages_consumed; + + // Local counter. + ++this->messages_consumed_; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) burst %d: message %d; overall message %d; message for this thread %d\n", + burst, + messages_in_this_burst, + total_messages_consumed, + this->messages_consumed_)); + } + } + + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + // I am no longer the leader. + leader_available = 0; + + // Wake up a follower. + this->condition_.signal (); + } + + if (exit_loop) + { + break; + } + else + { + // + // Process message here. + // + + for (size_t j = 0; j < message_size; ++j) + { + // Eat a little CPU + /* takes about 40.2 usecs on a 167 MHz Ultra2 */ + u_long n = 11UL; + ACE::is_prime (n, 2, n / 2); + } + + // + // Record stats for this message. + // + ACE_hrtime_t latency_from_start_of_burst = + ACE_OS::gethrtime () - start_of_burst; + this->latency_stats_.sample (latency_from_start_of_burst); + + this->throughput_stats_.sample (); + + if (debug >= PRINT_INDIVIDUAL_LATENCY) + { +#ifndef ACE_LACKS_LONGLONG_T + ACE_DEBUG ((LM_DEBUG, + "(%t) latency from start of burst: %Q\n", + latency_from_start_of_burst)); +#else + ACE_DEBUG ((LM_DEBUG, + "(%t) latency from start of burst: %u\n", + latency_from_start_of_burst.lo())); +#endif + } + } + } + + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("m:s:w:b:t:d:")); + int c; + + while ((c = get_opt ()) != -1) + { + switch (c) + { + case 'm': + number_of_messages = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': + message_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'w': + number_of_threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + burst_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + timeout_between_bursts = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = static_cast<DEBUGGING_RANGE> (ACE_OS::atoi (get_opt.opt_arg ())); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s\n" + "\t[-m number of messages]\n" + "\t[-s message size]\n" + "\t[-w number of threads]\n" + "\t[-b burst size]\n" + "\t[-t timeout between bursts]\n" + "\t[-d debug]\n", + argv[0]), + -1); + } + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int result = parse_args (argc, argv); + if (result != 0) + { + return result; + } + + move_to_rt_class (); + ACE_High_Res_Timer::calibrate (); + + ACE_SYNCH_MUTEX mutex; + ACE_SYNCH_CONDITION condition (mutex); + + // Leader Followers. + Leader_Follower_Task **leader_followers = 0; + ACE_NEW_RETURN (leader_followers, + Leader_Follower_Task *[number_of_threads], + -1); + + ACE_Profile_Timer timer; + timer.start (); + + int priority = + (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO) + + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO)) / 2; + + long flags = THR_BOUND | THR_SCHED_FIFO; + + // Create and activate them. + size_t i = 0; + for (i = 0; i < number_of_threads; ++i) + { + ACE_NEW_RETURN (leader_followers[i], + Leader_Follower_Task (mutex, + condition), + -1); + + // Activate the leader_followers. + result = leader_followers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + flags = THR_BOUND; + priority = ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + result = leader_followers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + return result; + } + } + } + + // Wait for all threads to terminate. + result = ACE_Thread_Manager::instance ()->wait (); + + timer.stop (); + ACE_Rusage rusage; + timer.elapsed_rusage (rusage); + + Latency_Stats latency; + Throughput_Stats throughput; + for (i = 0; i < number_of_threads; ++i) + { + latency.accumulate (leader_followers[i]->latency_stats_); + throughput.accumulate (leader_followers[i]->throughput_stats_); + ACE_DEBUG ((LM_DEBUG, "Thread[%d]: ", i)); + leader_followers[i]->throughput_stats_.dump_results (ACE_TEXT(""), ACE_TEXT("")); + } + + ACE_DEBUG ((LM_DEBUG, "\nTotals for latency:\n")); + latency.dump_results (argv[0], ACE_TEXT("latency")); + + ACE_DEBUG ((LM_DEBUG, "\nTotals for throughput:\n")); + throughput.dump_results (argv[0], ACE_TEXT("throughput")); + +#if defined(ACE_HAS_PRUSAGE_T) + ACE_DEBUG ((LM_DEBUG, "\n(%t) Context switches %d/%d\n", + rusage.pr_vctx, + rusage.pr_ictx)); +#endif /* ACE_HAS_PRUSAGE_T */ + + for (i = 0; i < number_of_threads; ++i) + { + delete leader_followers[i]; + } + delete[] leader_followers; + + return result; +} diff --git a/ACE/performance-tests/Server_Concurrency/Makefile.am b/ACE/performance-tests/Server_Concurrency/Makefile.am new file mode 100644 index 00000000000..5d8482ab96d --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Makefile.am @@ -0,0 +1,14 @@ +## 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 + +SUBDIRS = \ + Leader_Follower \ + Queue_Based_Workers + diff --git a/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Makefile.am b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Makefile.am new file mode 100644 index 00000000000..a2573d401c8 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Makefile.am @@ -0,0 +1,60 @@ +## 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.Svr_Conc_Queue_Based_Workers.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += workers + +workers_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +workers_SOURCES = \ + workers.cpp \ + RT_CORBA_Workers.h + +workers_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Svr_Conc_Queue_Based_Workers_RTCorba.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS += RT_CORBA_Workers + +RT_CORBA_Workers_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +RT_CORBA_Workers_SOURCES = \ + RT_CORBA_Workers.cpp \ + RT_CORBA_Workers.h + +RT_CORBA_Workers_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/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.cpp b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.cpp new file mode 100644 index 00000000000..543de4f60d9 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.cpp @@ -0,0 +1,591 @@ +// $Id$ +#include "RT_CORBA_Workers.h" + +#if defined (ACE_HAS_THREADS) + +#include "ace/OS_main.h" +#include "ace/ACE.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" +#include "ace/Lock_Adapter_T.h" + +// The number of messages that is being processed +static size_t number_of_messages = 100; + +// The number of upcall threads +static size_t number_of_workers = 2; + +// The size of the message +static size_t message_size = 100; + +// Number of threads that are ready to go +static size_t ready_threads = 0; + +// Number of input and output threads +static size_t io_threads = 2; // 1 for output and 1 for input + +// High resolution test timer +static ACE_High_Res_Timer test_timer; + +// Debugging condition +static DEBUGGING_RANGE debug = DEBUG_NONE; + +// Data block used by the message blocks +ACE_Data_Block *data_block = 0; + +/*******************************************************************/ +// Constructor for Synchronisers +Synchronisers::Synchronisers (void) + : mutex_ (), + event_ () +{ +} + + +int +Synchronisers::start_synchronization (void) +{ + // Hold the lock and increment the global variable to indicate + // number of ready threads + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + ready_threads ++; + + if (ready_threads == (number_of_workers + io_threads)) + { + // Reset the ready_threads so that we can wait at the end of + // runs + ready_threads = 0; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Ready to signal start \n")); + } + // Start the timer + test_timer.start (); + + // Signal all the threads + this->event_.signal (); + + // return to do our work; + return 0; + } + + // If we are not the last thread, let go off the lock + } + + if (debug) + { + //FUZZ: disable check_for_lack_ACE_OS + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Ready to wait () on event.. \n")); + //FUZZ: enable check_for_lack_ACE_OS + } + + // Wait blisfully till we are woken up + this->event_.wait (); + + return 0; +} + +int +Synchronisers::end_synchronization (void) +{ + // Hold the lock and increment the global variable to indicate + // number of ready threads + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, ace_mon, this->mutex_, -1); + + ready_threads ++; + + if (ready_threads == (number_of_workers + io_threads)) + { + // Reset the ready_threads so that we can wait at the end of + // runs + ready_threads = 0; + + // Start the timer + test_timer.stop (); + + // Signal all the threads + this->event_.signal (); + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Ended peacefully \n")); + } + + // return to do our work; + return 0; + } + + + // If we are not the last thread, let go off the lock + } + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Going to wait .. \n")); + } + + // Wait blisfully till we are woken up + this->event_.wait (); + + return 0; +} + +/*******************************************************************/ + +Worker_Task::Worker_Task (Message_Queue *mq, + Synchronisers &synch) + : ACE_Task<ACE_MT_SYNCH> (0, mq), + synch_ (synch), + messages_processed_ (0) +{ +} + +int +Worker_Task::svc (void) +{ + // Start synchronization + (void) this->synch_.start_synchronization (); + + for (;;) + { + ACE_Message_Block *mb = 0; + int result = this->getq (mb); + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Worker_Task::svc (%t) -> %p\n", + "getq error"), + -1); + } + + // Get the flag in the message blok + ACE_Message_Block::Message_Flags flag = + mb->self_flags (); + + // The stop flag + int stop_flag = 0; + + // Check for the stop flag + if (ACE_BIT_ENABLED (flag, + Synchronisers::MB_STOP_FLAG)) + { + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) saw flag after [%d] messages\n", + this->messages_processed_)); + } + + stop_flag = 1; + } + // Release the message block + mb->release (); + + // Counter. + ++this->messages_processed_; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) dequeued my %d message\n", + this->messages_processed_)); + } + + // + // Process message here. + // + + for (size_t j = 0; j < message_size; ++j) + { + // Eat a little CPU + /* takes about 40.2 usecs on a 167 MHz Ultra2 */ + u_long n = 11UL; + ACE::is_prime (n, 2, n / 2); + } + + // Make a message block for writing onto output queue + ACE_Message_Block *message_block = 0; + ACE_NEW_RETURN (message_block, + ACE_Message_Block (data_block), + -1); + + // Put this message block into the next queue or the output + // queue + result = this->put_next (message_block); + + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Input::svc (%t) -> %p\n", + "putq error"), + -1); + } + + // If the stop_flag is set just break and wait.. + if (stop_flag) + { + if (debug) + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Got stop message after [%d] messages \n", + this->messages_processed_)); + + break; + } + } + + (void) this->synch_.end_synchronization (); + return 0; +} + +int +Worker_Task::processed (void) +{ + return this->messages_processed_; +} + +/*******************************************************************/ + +Input_Task::Input_Task (Message_Queue *mq, + Synchronisers &synch) + : ACE_Task<ACE_MT_SYNCH> (0, mq), + synch_ (synch) +{ +} + +int +Input_Task::svc (void) +{ + // Synchronise threads + (void) this->synch_.start_synchronization (); + + + size_t i = 0; + for (i = 0; + i < (number_of_messages - number_of_workers); + ++i) + { + // Make a message block + ACE_Message_Block *message_block = 0; + ACE_NEW_RETURN (message_block, + ACE_Message_Block (data_block), + -1); + + int result = this->putq (message_block); + + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Input::svc (%t) -> %p\n", + "putq error"), + -1); + } + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) Input thread -> Sent [%d] messages\n", + i)); + } + } + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) Sending close messages \n")); + } + + + // Stop messages + for (i = 0; + i < number_of_workers; + ++i) + { + // Make a message block + ACE_Message_Block *message_block = 0; + ACE_NEW_RETURN (message_block, + ACE_Message_Block (data_block), + -1); + + // Set the stop flag in the message block and not in the datablock + message_block->set_self_flags (Synchronisers::MB_STOP_FLAG); + + int result = this->putq (message_block); + + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Input::svc (%t) -> %p\n", + "putq error"), + -1); + } + } + + + (void) this->synch_.end_synchronization (); + return 0; +} + +/*******************************************************************/ + +Output_Task::Output_Task (Message_Queue *mq, + Synchronisers &synch) + : ACE_Task<ACE_MT_SYNCH> (0, mq), + synch_ (synch) +{ +} + +int +Output_Task::svc (void) +{ + // Synchronise threads + (void) this->synch_.start_synchronization (); + + + for (size_t i = 0; + i < number_of_messages; + ++i) + { + // Get the message block from queue + ACE_Message_Block *mb = 0; + int result = this->getq (mb); + + // delete the message block + mb->release (); + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) Output thread -> received [%d] message\n", + i)); + } + + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Input::svc (%t) -> %p\n", + "putq error"), + -1); + } + } + + (void) this->synch_.end_synchronization (); + return 0; +} + +int +Output_Task::put (ACE_Message_Block *mb, ACE_Time_Value *) +{ + /* if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Sticking message into " + " output queue \n")); + }*/ + return this->putq (mb); +} + +/*******************************************************************/ + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("m:s:t:d:")); + int c; + + while ((c = get_opt ()) != -1) + { + switch (c) + { + case 'm': + number_of_messages = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + number_of_workers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = static_cast<DEBUGGING_RANGE> (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 's': + message_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s\n" + "\t[-m number of messages]\n" + "\t[-s message size]\n" + "\t[-w number of workers]\n" + "\t[-b burst size]\n" + "\t[-t timeout between bursts]\n" + "\t[-d debug]\n", + argv[0]), + -1); + } + } + + return 0; +} + + +/*******************************************************************/ +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int result = parse_args (argc, argv); + if (result != 0) + { + return result; + } + + ACE_High_Res_Timer::calibrate (); + + // Create the message queue + Message_Queue input_message_queue; + Message_Queue output_message_queue; + + // Create the datablocks. IF we use the default Message Blocks Ctor, + // it is going to do an extra allocation for the data block + ACE_NEW_RETURN (data_block, + ACE_Locked_Data_Block<ACE_Lock_Adapter<ACE_SYNCH_MUTEX> >, + -1); + + // Increment the reference count so that we can share the + // datablock. This is donw twice the number of messages for the + // input and output queues. + size_t i = 0; + + for (i = 0; i < 2*number_of_messages; ++i) + { + data_block->duplicate (); + } + + // Create the Synchronisers + Synchronisers synch; + + // Workers. + Worker_Task **workers = 0; + ACE_NEW_RETURN (workers, + Worker_Task *[number_of_workers], + -1); + + // Input Task + Input_Task input_task (&input_message_queue, + synch); + + // Output Task + Output_Task output_task (&output_message_queue, + synch); + int priority = + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO); + + + long flags = THR_SCHED_FIFO | THR_SCOPE_PROCESS; + + // Create and activate the worker threads + for (i = 0; i < number_of_workers; ++i) + { + ACE_NEW_RETURN (workers[i], + Worker_Task (&input_message_queue, synch), + -1); + + workers[i]->next (&output_task); + + // Activate the workers. + result = workers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + flags = THR_BOUND; + priority = ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + result = workers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + return result; + } + } + } + + + + // Activate the input and output threads + result = input_task.activate (flags, + 1, + 1, + priority); + + if (result != 0) + return result; + + + + // Activate the workers. + result = output_task.activate (flags, + 1, + 1, + priority); + + if (result != 0) + return result; + + + + // Wait for all threads to terminate. + result = ACE_Thread_Manager::instance ()->wait (); + + + ACE_hrtime_t elapsed_time = 0; + + test_timer.elapsed_time (elapsed_time); + +# if !defined (ACE_WIN32) + double elapsed_time_per_invocation = + (double) elapsed_time / number_of_messages; + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Throughput is [%f] \n", + elapsed_time_per_invocation)); + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Throughput is [%f] \n", + 1000000000/ elapsed_time_per_invocation)); + +#endif /*ACE_WIN32 */ + for (i = 0; i < number_of_workers; ++i) + { + ACE_DEBUG ((LM_DEBUG, + "Message process for thread [%d] is [%d] \n", + i, workers[i]->processed ())); + delete workers[i]; + } + delete[] workers; + + return result; +} + +#else /*ACE_HAS_THREADS*/ + +int +ACE_TMAIN(int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) Not supported in single threaded builds \n")); + + return 0; +} + + +#endif /*ACE_HAS_THREADS*/ diff --git a/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.h b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.h new file mode 100644 index 00000000000..46d78e8624c --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/RT_CORBA_Workers.h @@ -0,0 +1,166 @@ +/* -*- C++ -*- */ +//============================================================================= +/** + * @file RT_CORBA_Workers.h + * + * $Id$ + * + * The Workes test modified to suit RTCORBA tests. The original + * Workers has too many things that doesnt get what we are looking + * for (read it as just confusing enough). Hence a new test. + * + * @author Balachandran Natarajan <bala@cs.wustl.edu> + */ +//============================================================================= +#ifndef PERF_TEST_RT_CORBA_WORKERS_H +#define PERF_TEST_RT_CORBA_WORKERS_H +#include /**/ "ace/pre.h" + +#include "ace/Task_T.h" +#include "ace/Manual_Event.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#if defined (ACE_HAS_THREADS) + +enum DEBUGGING_RANGE +{ + DEBUG_NONE = 0, + DEBUG +}; + +typedef ACE_Message_Queue<ACE_MT_SYNCH> Message_Queue; + +/**************************************************************/ +/** + * @class Synchronisers + * + * @brief This class provides all the synchrnoisers used in this + * test. + * + * + */ + +/// Forward declaration.. +class IO_Task; +class Worker_Task; + +class Synchronisers +{ +public: + friend class IO_Task; + friend class Worker_Task; + + enum + { + /// Used in the message block to stop processing of message + /// blocks. + MB_STOP_FLAG = 0x2000 + }; + + /// Ctor + Synchronisers (void); + + int start_synchronization (void); + int end_synchronization (void); + +private: + + /// The mutex that is used for synchronisation. + ACE_SYNCH_MUTEX mutex_; + + /// A manual event + ACE_Manual_Event event_; +}; + + +/**************************************************************/ +/** + * @class Input_Task + * + * @brief Class that does the Input work ie. puts the events into the + * message queue + */ + +class Input_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + /// Ctor + Input_Task (Message_Queue *mq, + Synchronisers &synch); + + /// The thread runs inside this method.. + int svc (void); + +private: + + /// Our referance to Synchronisers + Synchronisers &synch_; +}; + +/**************************************************************/ +/** + * @class Output_Task + * + * @brief Class that does the Output work ie. getsx the events into the + * message queue + */ + +class Output_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + /// Ctor + Output_Task (Message_Queue *mq, + Synchronisers &synch); + + /// The thread runs inside this method.. + int svc (void); + + /// Need to overload this method to do anything useful.. + virtual int put (ACE_Message_Block *, ACE_Time_Value * = 0); + +private: + + /// Our referance to Synchronisers + Synchronisers &synch_; +}; + + +/**************************************************************/ +/** + * @class Synchronisers + * + * @brief This class provides and interface for all the upcall threads + * to run and do the actual work + */ + +class Worker_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + /// Ctor + Worker_Task (Message_Queue *mq, + Synchronisers &synch); + + /// Methods in which the threads run on + int svc (void); + + /// Processed messages + int processed (void); +private: + + /// Our referance to Synchronisers + Synchronisers &synch_; + + /// Number of messages processed by us + size_t messages_processed_; +}; + + + + +#endif /*ACE_HAS_THREADS*/ + +#include /**/ "ace/post.h" +#endif /*PERF_TEST_RT_CORBA_WORKERS_H*/ diff --git a/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Svr_Conc_Queue_Based_Workers.mpc b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Svr_Conc_Queue_Based_Workers.mpc new file mode 100644 index 00000000000..12566016d17 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/Svr_Conc_Queue_Based_Workers.mpc @@ -0,0 +1,18 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + avoids += ace_for_tao + exename = workers + source_files { + workers.cpp + } +} + +project(*RTCorba) : aceexe { + avoids += ace_for_tao + exename = RT_CORBA_Workers + source_files { + RT_CORBA_Workers.cpp + } +} diff --git a/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/workers.cpp b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/workers.cpp new file mode 100644 index 00000000000..5bb58d42397 --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/Queue_Based_Workers/workers.cpp @@ -0,0 +1,414 @@ +// $Id$ + +#include "ace/OS_main.h" +#include "ace/OS_NS_unistd.h" +#include "ace/ACE.h" +#include "ace/Task_T.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Sched_Params.h" +#include "ace/Profile_Timer.h" +#include "ace/Lock_Adapter_T.h" +#include "../Latency_Stats.h" + +static size_t number_of_messages = 100; +static size_t message_size = 100; +static size_t number_of_workers = 10; +static size_t burst_size = 10; +static size_t timeout_between_bursts = 1; + +enum DEBUGGING_RANGE +{ + DEBUG_NONE = 0, + DEFAULT = 1, + PRINT_INDIVIDUAL_LATENCY = 2 +}; + +static DEBUGGING_RANGE debug = DEBUG_NONE; + +static ACE_Data_Block *data_block = 0; + +class Message_Block : public ACE_Message_Block +{ +public: + Message_Block (ACE_Data_Block *data_block, + ACE_hrtime_t start_of_burst); + + ACE_hrtime_t start_of_burst_; +}; + +Message_Block::Message_Block (ACE_Data_Block *data_block, + ACE_hrtime_t start_of_burst) + : ACE_Message_Block (data_block), + start_of_burst_ (start_of_burst) +{ +} + +typedef ACE_Task<ACE_SYNCH> TASK; + +class Worker_Task : public TASK +{ +public: + Worker_Task (ACE_Message_Queue<ACE_SYNCH> *mq); + int svc (void); + + size_t messages_dequeued_; + + Latency_Stats latency_stats_; + Throughput_Stats throughput_stats_; +}; + +class IO_Task : public TASK +{ +public: + IO_Task (ACE_Message_Queue<ACE_SYNCH> *mq); + int svc (void); +}; + +Worker_Task::Worker_Task (ACE_Message_Queue<ACE_SYNCH> *mq) + : TASK (0, mq), + messages_dequeued_ (0) +{ +} + +int +Worker_Task::svc (void) +{ + for (;;) + { + ACE_Message_Block *mb = 0; + int result = this->getq (mb); + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "Worker_Task::svc (%t) -> %p\n", + "getq error"), + -1); + } + + ACE_Message_Block::ACE_Message_Type message_type = + mb->msg_type (); + + // If STOP message, break loop and end the task. + if (message_type == ACE_Message_Block::MB_STOP) + { + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) stop message dequeued after %d data messages\n", + this->messages_dequeued_)); + } + + mb->release (); + break; + } + + Message_Block *message_block = + dynamic_cast<Message_Block *> (mb); + + ACE_hrtime_t start_of_burst_for_this_message_block = + message_block->start_of_burst_; + + mb->release (); + + // Counter. + ++this->messages_dequeued_; + + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) dequeued its %d message\n", + this->messages_dequeued_)); + } + + // + // Process message here. + // + + for (size_t j = 0; j < message_size; ++j) + { + // Eat a little CPU + /* takes about 40.2 usecs on a 167 MHz Ultra2 */ + u_long n = 11UL; + ACE::is_prime (n, 2, n / 2); + } + + // + // Record stats for this message. + // + ACE_hrtime_t latency_from_start_of_burst = + ACE_OS::gethrtime () - start_of_burst_for_this_message_block; + this->latency_stats_.sample (latency_from_start_of_burst); + + this->throughput_stats_.sample (); + + if (debug >= PRINT_INDIVIDUAL_LATENCY) + { +#ifndef ACE_LACKS_LONGLONG_T + ACE_DEBUG ((LM_DEBUG, + "(%t) latency from start of burst: %Q\n", + latency_from_start_of_burst)); +#else + ACE_DEBUG ((LM_DEBUG, + "(%t) latency from start of burst: %u\n", + latency_from_start_of_burst.lo())); +#endif + } + } + + return 0; +} + +IO_Task::IO_Task (ACE_Message_Queue<ACE_SYNCH> *mq) + : TASK (0, mq) +{ +} + +int +IO_Task::svc (void) +{ + size_t i = 0; + size_t messages_queued = 1; + size_t burst = 1; + + // Data messages. + while (number_of_messages > 0) + { + ACE_hrtime_t start_of_burst = ACE_OS::gethrtime (); + + for (i = 1; + i <= burst_size && number_of_messages > 0; + ++i, --number_of_messages, ++messages_queued) + { + if (debug) + { + ACE_DEBUG ((LM_DEBUG, + "(%t) IO thread -> burst %d: message %d; overall message %d\n", + burst, + i, + messages_queued)); + } + + Message_Block *message_block = 0; + ACE_NEW_RETURN (message_block, + Message_Block (data_block, + start_of_burst), + -1); + + int result = this->putq (message_block); + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "IO::svc (%t) -> %p\n", + "putq error"), + -1); + } + } + + ++burst; + ACE_Time_Value tv (0, timeout_between_bursts); + ACE_OS::sleep (tv); + } + + // Terminate messages. + for (i = 0; i < number_of_workers; ++i) + { + ACE_Message_Block *message_block = 0; + ACE_NEW_RETURN (message_block, + ACE_Message_Block ((size_t)0, + (int)ACE_Message_Block::MB_STOP), + -1); + + int result = this->putq (message_block); + if (result == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "IO::svc (%t) -> %p\n", + "putq error"), + -1); + } + } + + return 0; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("m:s:w:b:t:d:")); + int c; + + while ((c = get_opt ()) != -1) + { + switch (c) + { + case 'm': + number_of_messages = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': + message_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'w': + number_of_workers = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'b': + burst_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': + timeout_between_bursts = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + debug = static_cast<DEBUGGING_RANGE> (ACE_OS::atoi (get_opt.opt_arg ())); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "usage: %s\n" + "\t[-m number of messages]\n" + "\t[-s message size]\n" + "\t[-w number of workers]\n" + "\t[-b burst size]\n" + "\t[-t timeout between bursts]\n" + "\t[-d debug]\n", + argv[0]), + -1); + } + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int result = parse_args (argc, argv); + if (result != 0) + { + return result; + } + + move_to_rt_class (); + ACE_High_Res_Timer::calibrate (); + + size_t i = 0; + + ACE_NEW_RETURN (data_block, + ACE_Locked_Data_Block<ACE_Lock_Adapter<ACE_SYNCH_MUTEX> >, + -1); + + for (i = 0; i < number_of_messages; ++i) + { + data_block->duplicate (); + } + + ACE_Message_Queue<ACE_SYNCH> message_queue; + + // Workers. + Worker_Task **workers = 0; + ACE_NEW_RETURN (workers, + Worker_Task *[number_of_workers], + -1); + + ACE_Profile_Timer timer; + timer.start (); + + int priority = + (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO) + + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO)) / 2; + // priority = ACE_Sched_Params::next_priority (ACE_SCHED_FIFO, priority); + + long flags = THR_BOUND | THR_SCHED_FIFO; + + // Create and activate them. + for (i = 0; i < number_of_workers; ++i) + { + ACE_NEW_RETURN (workers[i], + Worker_Task (&message_queue), + -1); + + // Activate the workers. + result = workers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + flags = THR_BOUND; + priority = ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + result = workers[i]->activate (flags, + 1, + 1, + priority); + if (result != 0) + { + return result; + } + } + } + + // IO Task. + IO_Task io (&message_queue); + + // Activate the workers. + priority = + (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO) + + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO)) / 2; + priority = ACE_Sched_Params::next_priority (ACE_SCHED_FIFO, priority); + + flags = THR_BOUND | THR_SCHED_FIFO; + + result = io.activate (THR_BOUND); + if (result != 0) + { + flags = THR_BOUND; + priority = ACE_Sched_Params::priority_min (ACE_SCHED_OTHER, + ACE_SCOPE_THREAD); + result = io.activate (flags, + 1, + 1, + priority); + if (result != 0) + { + return result; + } + } + + // Wait for all threads to terminate. + result = ACE_Thread_Manager::instance ()->wait (); + + timer.stop (); + ACE_Rusage rusage; + timer.elapsed_rusage (rusage); + + Latency_Stats latency; + Throughput_Stats throughput; + for (i = 0; i < number_of_workers; ++i) + { + latency.accumulate (workers[i]->latency_stats_); + throughput.accumulate (workers[i]->throughput_stats_); + ACE_DEBUG ((LM_DEBUG, "Thread[%d]: ", i)); + workers[i]->throughput_stats_.dump_results (ACE_TEXT(""), ACE_TEXT("")); + } + + ACE_DEBUG ((LM_DEBUG, "\nTotals for latency:\n")); + latency.dump_results (argv[0], ACE_TEXT("latency")); + + ACE_DEBUG ((LM_DEBUG, "\nTotals for throughput:\n")); + throughput.dump_results (argv[0], ACE_TEXT("throughput")); + +#if defined(ACE_HAS_PRUSAGE_T) + ACE_DEBUG ((LM_DEBUG, "\n(%t) Context switches %d/%d\n", + rusage.pr_vctx, + rusage.pr_ictx)); +#endif /* ACE_HAS_PRUSAGE_T */ + + for (i = 0; i < number_of_workers; ++i) + { + delete workers[i]; + } + delete[] workers; + delete data_block; + + return result; +} + diff --git a/ACE/performance-tests/Server_Concurrency/run_test.sh b/ACE/performance-tests/Server_Concurrency/run_test.sh new file mode 100755 index 00000000000..0f45ca8f11a --- /dev/null +++ b/ACE/performance-tests/Server_Concurrency/run_test.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# $Id$ +# + +for s in 0 25 50 75 100; do + for w in 1 2 3 4 5 6; do + echo ================ WORK: $s 1>&2 + echo ================ THREADS: $w 1>&2 + ./Queue_Based_Workers/workers -m 100000 -b 100000 -s $s -w $w + ./Leader_Follower/leader_follower -m 100000 -b 100000 -s $s -w $w + done +done diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.cpp b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.cpp new file mode 100644 index 00000000000..48d57269f18 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.cpp @@ -0,0 +1,237 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL + +#include "Baseline_Test.h" + +# if defined (ACE_HAS_THREADS) + +#include "ace/OS_NS_unistd.h" +#include "ace/Service_Repository.h" +#include "ace/Get_Opt.h" +#include "ace/Thread_Manager.h" + +#if !defined (__ACE_INLINE__) +#include "Baseline_Test.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID (Base_Test, + Baseline_Test, + "$Id$") + +Baseline_Test_Options baseline_options; +// Static Baseline Options holds the test configuration information +// and the test statistics. + +Baseline_Test_Base::Baseline_Test_Base (void) + : Benchmark_Base (Benchmark_Base::BASELINE), + yield_method_ (Baseline_Test_Options::USE_SLEEP_ZERO), + iteration_ (DEFAULT_ITERATIONS), + what_(TEST_LOCK) +{ +} + +int +Baseline_Test_Base::init (int argc, ACE_TCHAR *argv[]) +{ + return this->parse_args (argc, argv); +} + +int +Baseline_Test_Base::parse_args (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("i:ylrw"), 0); + int c; + + while ((c = getopt ()) != -1) + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 'i': // Total iterations + { + int tmp = ACE_OS::atoi (getopt.opt_arg ()); + if (tmp <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%d is not a valid value for iteration\n", + tmp), -1); + else + this->iteration_ = static_cast<size_t> (tmp); + } + break; + + case 'y': // Use thr_yield. + this->yield_method_ = Baseline_Test_Options::USE_THR_YIELD; + break; + + case 'l': + this->what_ = TEST_LOCK; + break; + + case 'r': + this->what_ = TEST_READLOCK; + break; + + case 'w': + this->what_ = TEST_WRITELOCK; + break; + + default: + ACE_ERROR ((LM_ERROR, "Invalid argument %c used\n", c)); + break; + } + return 0; +} + +void +Baseline_Test_Base::yield (void) +{ + if (this->yield_method_ == Baseline_Test_Options::USE_SLEEP_ZERO) + ACE_OS::sleep (0); + else + ACE_OS::thr_yield (); +} + +Baseline_Test_Options::Baseline_Test_Options (void) + : test_try_lock_ (0), + verbose_ (0), + current_yield_method_ (0), + current_iteration_ (0), + total_iteration_ (DEFAULT_ITERATIONS) +{ +} + +int +Baseline_Test_Options::parse_args (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("tv"), 0); + int c; + + while ((c = getopt ()) != -1) + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { + case 't': + this->test_try_lock_ = 1; + break; + + case 'v': + this->verbose_ = 1; + break; + + default: + ACE_ERROR ((LM_ERROR, "Invalid arguemnt %c used.\n", c)); + break; + } + return 0; +} + +int +Baseline_Test_Options::reset_params (size_t iteration, + int yield) +{ + this->current_iteration_ = 0; + this->timer.reset (); + + this->current_yield_method_ = yield; + this->total_iteration_ = iteration; + return 0; +} + +void +Baseline_Test_Options::print_result (void) +{ + ACE_Time_Value tv; + ACE_hrtime_t nsec; + + this->timer.elapsed_time_incr (tv); + this->timer.elapsed_time_incr (nsec); + ACE_DEBUG ((LM_DEBUG, + "Total Time: %d sec %d usec for a " + "total of %d iterations\n" + "Average time: %d nanoseconds.\n", + tv.sec (), tv.usec (), + this->current_iteration_, + (int) (nsec / this->current_iteration_))); +} + +Baseline_Test::Baseline_Test (void) + : current_test_ (0), + get_lock_ (2), + let_go_lock_ (2) +{ +} + +// Initialize and run the benchmarks tests. + +int +Baseline_Test::init (int argc, ACE_TCHAR **argv) +{ + return baseline_options.parse_args (argc, argv); +} + +int +Baseline_Test::pre_run_test (Benchmark_Base *bb) +{ + this->current_test_ = (Baseline_Test_Base *) bb; + baseline_options.reset_params (this->current_test_->iteration (), + this->current_test_->yield_method ()); + if (baseline_options.test_try_lock ()) + { + ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (Baseline_Test::hold_lock), + (void *) this); + + this->get_lock_.wait (); + // Wait until the lock is held by the spawning thread. + } + + return 0; +} + +int +Baseline_Test::run_test (void) +{ + if (baseline_options.test_try_lock ()) + return this->current_test_->test_try_lock (); + else + return this->current_test_->test_acquire_release (); +} + +int +Baseline_Test::post_run_test (void) +{ + if (baseline_options.test_try_lock ()) + { + // Release the lock we hold. + this->let_go_lock_.wait (); + + ACE_Thread_Manager::instance ()->wait (); + } + + baseline_options.print_result (); + + return 0; +} + +int +Baseline_Test::valid_test_object (Benchmark_Base *bb) +{ + return (bb->benchmark_type () == Benchmark_Base::BASELINE); +} + +void * +Baseline_Test::hold_lock (void *arg) +{ + Baseline_Test *this_test = (Baseline_Test *) arg; + this_test->current_test_->acquire (); + this_test->get_lock_.wait (); + + this_test->let_go_lock_.wait (); + return 0; +} + +ACE_SVC_FACTORY_DEFINE (Baseline_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.h b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.h new file mode 100644 index 00000000000..2896833f60e --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.h @@ -0,0 +1,153 @@ +// -*- C++ -*- +// $Id$ + +#ifndef ACE_BASELINE_TEST_H +#define ACE_BASELINE_TEST_H + +#include "Synch_Lib/Benchmark_Base.h" + +#if defined (ACE_HAS_THREADS) + +#include "ace/Profile_Timer.h" +#include "ace/svc_export.h" +#include "ace/Barrier.h" + +const unsigned long DEFAULT_ITERATIONS = 1000000; + +class ACE_Svc_Export Baseline_Test_Base : public Benchmark_Base +{ + // = TITLE + // This class identifies itself as Benmarking Performance Test class. +public: + enum { + TEST_LOCK, + TEST_READLOCK, + TEST_WRITELOCK + }; + + virtual int init (int argc, ACE_TCHAR *argv[]); + // Standard initializing method for Baseline Test. + + int parse_args (int argc, ACE_TCHAR *argv[]); + // Parsing the svc.conf file arguments. + + virtual int acquire () = 0; + virtual int release () = 0; + // These two method are used to test try_acquire performance. + + virtual int test_acquire_release () = 0; + virtual int test_try_lock () = 0; + // Real test methods. + + virtual int yield_method (); + // Query the yield method used. + + virtual void yield (); + // Yield to other thread. + + size_t iteration (void); + // Access methods. + +protected: + Baseline_Test_Base (void); + + int yield_method_; + // Should we your thr_yield or sleep (0). + + size_t iteration_; + // Total number of operations. <iterations_> + + int what_; + // What test should be performed? +}; + +class ACE_Svc_Export Baseline_Test_Options +{ + // = TITLE + // This class holds the global settings for Baseline Test. +public: + friend class Baseline_Test; + + enum + { + USE_SLEEP_ZERO, + USE_THR_YIELD + }; + + Baseline_Test_Options (void); + // ctor. + + int parse_args (int argc, ACE_TCHAR *argv[]); + // Parse and set the Baseline_Test options and flags. + + int reset_params (size_t iteration, int yield); + // Reset test parameters for next round. + + int test_try_lock (void); + // Return test configuration. + + void start_inc_timer (void); + void stop_inc_timer (void); + // Start/stop measuring time. + + int inc_loop_counter (void); + // Returns + + size_t current_iteration (void); + // Return <iteration_>. + + void print_result (void); + // Print out the result. + +private: + int test_try_lock_; + // A flag indicates whether we are testing try_lock or lock and + // release. + + int verbose_; + // Print out the result in verbose mode. + + int current_yield_method_; + // yield or sleep. + + size_t current_iteration_; + // Number of iteration. + + size_t total_iteration_; + // Total number of target iteration. + + ACE_High_Res_Timer timer; + // Profile timer result. +}; + +extern Baseline_Test_Options baseline_options; + +class ACE_Svc_Export Baseline_Test : public Benchmark_Method_Base +{ +public: + Baseline_Test (void); + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int pre_run_test (Benchmark_Base *bp); + virtual int run_test (void); + virtual int post_run_test (void); + virtual int valid_test_object (Benchmark_Base *); + + static void *hold_lock (void * arg); + // This method runs in a separate thread, and is used to hold the lock while + // we test the performance of try lock. + +private: + Baseline_Test_Base *current_test_; + ACE_Barrier get_lock_; + ACE_Barrier let_go_lock_; +}; + +ACE_SVC_FACTORY_DECLARE (Baseline_Test) + +#if defined (__ACE_INLINE__) +#include "Baseline_Test.inl" +#endif /* __ACE_INLINE__ */ + +#endif /* ACE_HAS_THREADS */ + +#endif /* ACE_BASELINE_TEST_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.inl b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.inl new file mode 100644 index 00000000000..05aa007478d --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Baseline_Test.inl @@ -0,0 +1,43 @@ +// $Id$ + +ACE_INLINE size_t +Baseline_Test_Base::iteration (void) +{ + return this->iteration_; +} + +ACE_INLINE int +Baseline_Test_Base::yield_method (void) +{ + return this->yield_method_; +} + +ACE_INLINE int +Baseline_Test_Options::test_try_lock (void) +{ + return this->test_try_lock_; +} + +ACE_INLINE size_t +Baseline_Test_Options::current_iteration (void) +{ + return this->current_iteration_; +} + +ACE_INLINE void +Baseline_Test_Options::start_inc_timer (void) +{ + this->timer.start_incr (); +} + +ACE_INLINE void +Baseline_Test_Options::stop_inc_timer (void) +{ + this->timer.stop_incr (); +} + +ACE_INLINE int +Baseline_Test_Options::inc_loop_counter (void) +{ + return (++this->current_iteration_ < this->total_iteration_); +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/Makefile.am b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Makefile.am new file mode 100644 index 00000000000..f5f4158d270 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Makefile.am @@ -0,0 +1,52 @@ +## 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) + + +## Makefile.Synch_Benchmarks_Base_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_LTLIBRARIES = libBase_Test.la + +libBase_Test_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -I$(srcdir)/.. + +libBase_Test_la_SOURCES = \ + Baseline_Test.cpp \ + base_test.cpp \ + mutex_test.cpp + +../Synch_Lib: + mkdir -p ../Synch_Lib + +libBase_Test_la_DEPENDENCIES = \ + ../Synch_Lib + +libBase_Test_la_LDFLAGS = \ + -L../Synch_Lib + +noinst_HEADERS = \ + Baseline_Test.h \ + Baseline_Test.inl + +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/Synch-Benchmarks/Base_Test/Synch_Benchmarks_Base_Test.mpc b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Synch_Benchmarks_Base_Test.mpc new file mode 100644 index 00000000000..47d16a47bb3 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/Synch_Benchmarks_Base_Test.mpc @@ -0,0 +1,16 @@ +// -*- MPC -*- +// $Id$ + +project : acelib { + sharedname = Base_Test + avoids += ace_for_tao + after += Synch_Lib + libs += Synch_Lib + libpaths += ../Synch_Lib + + specific (automake) { + includes += $(srcdir)/.. + } else { + includes += .. + } +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/base_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Base_Test/base_test.cpp new file mode 100644 index 00000000000..6d3622c27b8 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/base_test.cpp @@ -0,0 +1,62 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL + +#include "Baseline_Test.h" + +# if defined (ACE_HAS_THREADS) + +#include "ace/Log_Msg.h" + +class ACE_Svc_Export Baseline_Base_Test : public Baseline_Test_Base +{ +public: + virtual int acquire (); + virtual int release (); + // These two method are used to test try_acquire performance. + + virtual int test_acquire_release (); + virtual int test_try_lock (); + // Real test methods. +}; + +int +Baseline_Base_Test::acquire () +{ + return 0; +} + +int +Baseline_Base_Test::release () +{ + return 0; +} + +int +Baseline_Base_Test::test_acquire_release () +{ + baseline_options.start_inc_timer (); + + for (; baseline_options.inc_loop_counter () ; ) + ; + + baseline_options.stop_inc_timer (); + return 0; +} + +int +Baseline_Base_Test::test_try_lock () +{ + baseline_options.start_inc_timer (); + + for (; baseline_options.inc_loop_counter () ; ) + ; + + baseline_options.stop_inc_timer (); + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Baseline_Base_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Base_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Base_Test/mutex_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Base_Test/mutex_test.cpp new file mode 100644 index 00000000000..162f60943f6 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Base_Test/mutex_test.cpp @@ -0,0 +1,205 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL + +#include "Baseline_Test.h" + +# if defined (ACE_HAS_THREADS) + +#include "ace/Log_Msg.h" +#include "ace/Token.h" +#include "ace/Process_Mutex.h" +#include "ace/RW_Mutex.h" +#include "ace/RW_Process_Mutex.h" +#include "ace/RW_Thread_Mutex.h" +#include "ace/Lock_Adapter_T.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Semaphore.h" +#include "ace/Null_Semaphore.h" +#include "ace/Process_Semaphore.h" + + +template<class LOCK> +class ACE_Svc_Export Baseline_Lock_Test : public Baseline_Test_Base +{ +public: + virtual int acquire (); + virtual int release (); + // These two method are used to test try_acquire performance. + + virtual int test_acquire_release (); + virtual int test_try_lock (); + // Real test methods. + +private: + LOCK lock_; + // +}; + +template<class LOCK> int +Baseline_Lock_Test<LOCK>::acquire () +{ + int retv = 0; + switch (this->what_) + { + case TEST_READLOCK: + retv = this->lock_.acquire_read (); + break; + case TEST_WRITELOCK: + retv = this->lock_.acquire_write (); + break; + case TEST_LOCK: + default: + retv = this->lock_.acquire (); + break; + } + + return retv; +} + +template<class LOCK> int +Baseline_Lock_Test<LOCK>::release () +{ + return this->lock_.release (); +} + +template<class LOCK> int +Baseline_Lock_Test<LOCK>::test_acquire_release () +{ + baseline_options.start_inc_timer (); + + switch (this->what_) + { + case TEST_READLOCK: + for (; baseline_options.inc_loop_counter () ; ) + { + this->lock_.acquire_read (); + this->lock_.release (); + } + break; + case TEST_WRITELOCK: + for (; baseline_options.inc_loop_counter () ; ) + { + this->lock_.acquire_write (); + this->lock_.release (); + } + break; + case TEST_LOCK: + default: + for (; baseline_options.inc_loop_counter () ; ) + { + this->lock_.acquire (); + this->lock_.release (); + } + break; + } + + baseline_options.stop_inc_timer (); + + return 0; +} + +template<class LOCK> int +Baseline_Lock_Test<LOCK>::test_try_lock () +{ + baseline_options.start_inc_timer (); + switch (this->what_) + { + case TEST_READLOCK: + for (; baseline_options.inc_loop_counter () ; ) + this->lock_.tryacquire_read (); // This should always fail. + break; + case TEST_WRITELOCK: + for (; baseline_options.inc_loop_counter () ; ) + this->lock_.tryacquire_write (); // This should always fail. + break; + case TEST_LOCK: + default: + for (; baseline_options.inc_loop_counter () ; ) + this->lock_.tryacquire (); // This should always fail. + break; + } + + for (; baseline_options.inc_loop_counter () ; ) + this->lock_.tryacquire (); // This should always fail. + + baseline_options.stop_inc_timer (); + return 0; +} + +typedef Baseline_Lock_Test<ACE_Thread_Mutex> Baseline_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_RW_Thread_Mutex> Baseline_RW_Thread_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_RW_Thread_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_RW_Thread_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_RW_Mutex> Baseline_RW_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_RW_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_RW_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_Process_Mutex> Baseline_Process_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Process_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Process_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_RW_Process_Mutex> Baseline_RW_Process_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_RW_Process_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_RW_Process_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_Null_Mutex> Baseline_Null_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Null_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Null_Mutex_Test) + +typedef Baseline_Lock_Test< ACE_Lock_Adapter<ACE_Null_Mutex> > Baseline_Adaptive_Null_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Adaptive_Null_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Adaptive_Null_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_Recursive_Thread_Mutex> Baseline_Recursive_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Recursive_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Recursive_Mutex_Test) + +typedef Baseline_Lock_Test< ACE_Lock_Adapter<ACE_Thread_Mutex> > Baseline_Adaptive_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Adaptive_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Adaptive_Mutex_Test) + +typedef Baseline_Lock_Test< ACE_Lock_Adapter<ACE_RW_Mutex> > Baseline_Adaptive_RW_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Adaptive_RW_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Adaptive_RW_Mutex_Test) + +typedef Baseline_Lock_Test< ACE_Lock_Adapter<ACE_Recursive_Thread_Mutex> > Baseline_Adaptive_Recursive_Mutex_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Adaptive_Recursive_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Adaptive_Recursive_Mutex_Test) + +typedef Baseline_Lock_Test<ACE_Semaphore> Baseline_Semaphore_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Semaphore_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Semaphore_Test) + +typedef Baseline_Lock_Test<ACE_Semaphore> Baseline_Process_Semaphore_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Process_Semaphore_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Process_Semaphore_Test) + +typedef Baseline_Lock_Test<ACE_Null_Semaphore> Baseline_Null_Semaphore_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Null_Semaphore_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Null_Semaphore_Test) + +typedef Baseline_Lock_Test<ACE_Token> Baseline_Token_Test; + +ACE_SVC_FACTORY_DECLARE (Baseline_Token_Test) +ACE_SVC_FACTORY_DEFINE (Baseline_Token_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Makefile.am b/ACE/performance-tests/Synch-Benchmarks/Makefile.am new file mode 100644 index 00000000000..068a35d9c61 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Makefile.am @@ -0,0 +1,44 @@ +## 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) + +SUBDIRS = \ + Synch_Lib \ + Perf_Test \ + Base_Test \ + . + +## Makefile.Synch_Benchmarks.am + +noinst_PROGRAMS = synch_driver + +synch_driver_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +synch_driver_SOURCES = \ + synch_driver.cpp + +synch_driver_LDFLAGS = \ + -LSynch_Lib + +synch_driver_LDADD = \ + $(top_builddir)/performance-tests/Synch-Benchmarks/Synch_Lib/libSynch_Lib.la \ + $(ACE_BUILDDIR)/ace/libACE.la + +## 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/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.cpp new file mode 100644 index 00000000000..32ad7ea5431 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.cpp @@ -0,0 +1,48 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Adaptive_Lock_Performance_Test_Base.h" + +ACE_RCSID(Synch_Benchmarks, Adaptive_Lock_Performance_Test_Base_, "$Id$") + +#if defined (ACE_HAS_THREADS) + +ACE_Lock *Adaptive_Lock_Performance_Test_Base::lock_ = 0; + +int +Adaptive_Lock_Performance_Test_Base::fini (void) +{ + delete Adaptive_Lock_Performance_Test_Base::lock_; + Adaptive_Lock_Performance_Test_Base::lock_ = 0; + return 0; +} + +int +Adaptive_Lock_Performance_Test_Base::svc (void) +{ + // Extract out the unique thread-specific value to be used as an + // index... + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + Adaptive_Lock_Performance_Test_Base::lock_->acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + Adaptive_Lock_Performance_Test_Base::lock_->release (); + } + /* NOTREACHED */ + return 0; +} + +int +Adaptive_Lock_Performance_Test_Base::set_lock (ACE_Lock *lock) +{ + delete Adaptive_Lock_Performance_Test_Base::lock_; + Adaptive_Lock_Performance_Test_Base::lock_ = lock; + return (Adaptive_Lock_Performance_Test_Base::lock_ != 0 ? 0 : -1); +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.h b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.h new file mode 100644 index 00000000000..9fb65628ea8 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Adaptive_Lock_Performance_Test_Base.h @@ -0,0 +1,24 @@ +// $Id$ + +#ifndef ACE_ADAPTIVE_LOCK_PERFORMANCE_TEST_BASE_H +#define ACE_ADAPTIVE_LOCK_PERFORMANCE_TEST_BASE_H +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, Adaptive_Lock_Performance_Test_Base, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Adaptive_Lock_Performance_Test_Base : public Benchmark_Performance +{ +public: + virtual int init (int, ACE_TCHAR *[]) = 0; + virtual int fini (void); + virtual int svc (void); + + int set_lock (ACE_Lock *lock); + +private: + static ACE_Lock *lock_; +}; +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_ADAPTIVE_LOCK_PERFORMANCE_TEST_BASE_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.cpp new file mode 100644 index 00000000000..48fe88cad2f --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.cpp @@ -0,0 +1,60 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Benchmark_Performance.h" +#include "ace/Basic_Types.h" + +ACE_RCSID(Synch_Benchmarks, Benchmark_Performance, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Global variables (used by the dynamically linked services). +ACE_Svc_Export int synch_count; +int buffer; + +// Initialize the static variables. +/* static */ +sig_atomic_t Benchmark_Performance::done_ = 0; + +Benchmark_Performance_Test_Base::Benchmark_Performance_Test_Base (void) + : Benchmark_Base (Benchmark_Base::PERFORMANCE) +{ +} + +sig_atomic_t +Benchmark_Performance::done (void) +{ + return Benchmark_Performance::done_; +} + +void +Benchmark_Performance::done (sig_atomic_t d) +{ + Benchmark_Performance::done_ = d; +} + +int +Benchmark_Performance::init (int, ACE_TCHAR **) +{ + return 1; +} + +int +Benchmark_Performance::info (ACE_TCHAR **, size_t) const +{ + return -1; +} + +int +Benchmark_Performance::fini (void) +{ + return -1; +} + +void * +Benchmark_Performance::svc_run (Benchmark_Performance *bp) +{ + return (void *) (bp->svc () == -1 ? (intptr_t) -1 : (intptr_t) 0); +} + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.h b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.h new file mode 100644 index 00000000000..b66a83048a7 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Benchmark_Performance.h @@ -0,0 +1,48 @@ +/* -*- C++ -*- */ +// $Id$ + +/* Defines the class used to dynamically link in the benchmark tests */ + +#ifndef ACE_BENCHMARK_PERFORMANCE_H +#define ACE_BENCHMARK_PERFORMANCE_H + +#include "Synch_Lib/Benchmark_Base.h" + +#if defined (ACE_HAS_THREADS) + +#include "ace/svc_export.h" + +extern int buffer; +extern ACE_Svc_Export int synch_count; + +class ACE_Svc_Export Benchmark_Performance_Test_Base : public Benchmark_Base +{ + // = TITLE + // This class identifies itself as Benmarking Performance Test class. +protected: + Benchmark_Performance_Test_Base (void); +}; + +class ACE_Svc_Export Benchmark_Performance : public Benchmark_Performance_Test_Base +{ + // = TITLE + // Base class for all the timing tests. +public: + // = Hooks inherited from ACE_Service_Object. + virtual int svc (void) = 0; + virtual int init (int, ACE_TCHAR *[]); + virtual int info (ACE_TCHAR **, size_t) const; + virtual int fini (void); + static void *svc_run (Benchmark_Performance *bp); + + // = Set/get flag that controls how the tests are shut down + // gracefully. + static void done (sig_atomic_t); + static sig_atomic_t done (void); + +protected: + static sig_atomic_t done_; + // Keeps track if we are finished or not. +}; +#endif /* ACE_HAS_THREADS */ +#endif /* ACE_BENCHMARK_PERFORMANCE_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Makefile.am b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Makefile.am new file mode 100644 index 00000000000..ac4f8fc9a00 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Makefile.am @@ -0,0 +1,73 @@ +## 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) + + +## Makefile.Synch_Benchmarks_Perf_Test.am + +if !BUILD_ACE_FOR_TAO + +noinst_LTLIBRARIES = libPerf_Test.la + +libPerf_Test_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -I$(srcdir)/.. + +libPerf_Test_la_SOURCES = \ + Adaptive_Lock_Performance_Test_Base.cpp \ + Benchmark_Performance.cpp \ + Performance_Test.cpp \ + Performance_Test_Options.cpp \ + adaptive_mutex_test.cpp \ + adaptive_recursive_lock_test.cpp \ + adaptive_sema_test.cpp \ + condb_test.cpp \ + conds_test.cpp \ + context_test.cpp \ + guard_test.cpp \ + memory_test.cpp \ + mutex_test.cpp \ + pipe_proc_test.cpp \ + pipe_thr_test.cpp \ + recursive_lock_test.cpp \ + rwrd_test.cpp \ + rwwr_test.cpp \ + sema_test.cpp \ + sysvsema_test.cpp \ + token_test.cpp + +../Synch_Lib: + mkdir -p ../Synch_Lib + +libPerf_Test_la_DEPENDENCIES = \ + ../Synch_Lib + +libPerf_Test_la_LDFLAGS = \ + -L../Synch_Lib + +noinst_HEADERS = \ + Adaptive_Lock_Performance_Test_Base.h \ + Benchmark_Performance.h \ + Performance_Test.h \ + Performance_Test_Options.h \ + Performance_Test_Options.inl + +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/Synch-Benchmarks/Perf_Test/Performance_Test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test.cpp new file mode 100644 index 00000000000..42045f5963e --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test.cpp @@ -0,0 +1,122 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL + +#include "Performance_Test.h" + +# if defined (ACE_HAS_THREADS) + +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +#include "ace/Service_Repository.h" +#include "ace/Reactor.h" + +ACE_RCSID (Perf_Test, + Performance_Test, + "$Id$") + +Performance_Test::Performance_Test (void) + : n_lwps_ (0), + orig_n_lwps_ (0) +{ +} + +// Initialize and run the benchmarks tests. + +int +Performance_Test::init (int argc, ACE_TCHAR **argv) +{ + ACE_DEBUG ((LM_DEBUG, "Performance_Test::init\n")); + performance_test_options.parse_args (argc, argv); + return 0; +} + +int +Performance_Test::pre_run_test (Benchmark_Base *bb) +{ + this->orig_n_lwps_ = ACE_Thread::getconcurrency (); + this->n_lwps_ = performance_test_options.n_lwps (); + Benchmark_Performance *bp = (Benchmark_Performance *) bb; + + if (this->n_lwps_ > 0) + ACE_Thread::setconcurrency (this->n_lwps_); + + // We should probably use a "barrier" here rather than + // THR_SUSPENDED since many OS platforms lack the ability to + // create suspended threads... + if (ACE_Thread_Manager::instance ()->spawn_n + (performance_test_options.thr_count (), ACE_THR_FUNC (bp->svc_run), + (void *) bp, performance_test_options.t_flags () | THR_SUSPENDED) == -1) + ACE_ERROR ((LM_ERROR, "%p\n%a", "couldn't spawn threads", 1)); + return 0; +} + +int +Performance_Test::run_test (void) +{ + // Tell the threads that we are not finished. + Benchmark_Performance::done (0); + + // Allow thread(s) to make progress. + ACE_Thread_Manager::instance ()->resume_all (); + + ACE_Time_Value timeout (performance_test_options.sleep_time ()); + + ACE_DEBUG ((LM_DEBUG, "starting timer\n")); + performance_test_options.start_timer (); + + // Use Reactor as a timer (which can be interrupted by a signal). + ACE_Reactor::run_event_loop (timeout); + + performance_test_options.stop_timer (); + ACE_DEBUG ((LM_DEBUG, "\nstopping timer\n")); + + return 0; +} + +int +Performance_Test::post_run_test (void) +{ + // Stop thread(s) from making any further progress. + ACE_Thread_Manager::instance ()->suspend_all (); + + // Tell the threads that we are finished. + Benchmark_Performance::done (1); + + ACE_DEBUG ((LM_DEBUG, "------------------------------------------------------------------------\n")); + ACE_DEBUG ((LM_DEBUG, "targ 0x%x (%s, %s, %s)\n" + "n_lwps_orig = %d, n_lwps_set = %d, n_lwps_end = %d\n", + performance_test_options.t_flags (), + (performance_test_options.t_flags () & THR_DETACHED) ? "THR_DETACHED" : "Not Detached", + (performance_test_options.t_flags () & THR_BOUND) ? "THR_BOUND" : "Not Bound", + (performance_test_options.t_flags () & THR_NEW_LWP) ? "THR_NEW_LWP" : "No New_LWP", + this->orig_n_lwps_, this->n_lwps_, ACE_Thread::getconcurrency ())); + int count = performance_test_options.count (); + float rate = count / (float (performance_test_options.sleep_time ())); + + ACE_DEBUG ((LM_DEBUG, + "to count = %d\nrate = %.3f ops/sec, per operation = %.2f usec\n", + count, + rate, + (1.0e6 / rate) / synch_count)); + performance_test_options.print_results (); + // Allow thread(s) to finish up. + ACE_Thread_Manager::instance ()->resume_all (); + + // Wait for all the threads to exit. + ACE_Thread_Manager::instance ()->wait (); + performance_test_options.init (); + + return 0; +} + +int +Performance_Test::valid_test_object (Benchmark_Base *bb) +{ + return (bb->benchmark_type () == Benchmark_Base::PERFORMANCE); +} + +ACE_SVC_FACTORY_DEFINE (Performance_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test.h b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test.h new file mode 100644 index 00000000000..f56e8001e76 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test.h @@ -0,0 +1,32 @@ +// -*- C++ -*- +// +// $Id$ + +#ifndef ACE_PERFORMANCE_TEST_H +#define ACE_PERFORMANCE_TEST_H + +#include "ace/svc_export.h" + +#if defined (ACE_HAS_THREADS) + +#include "Synch_Lib/Benchmark_Base.h" + +class ACE_Svc_Export Performance_Test : public Benchmark_Method_Base +{ +public: + Performance_Test (void); + virtual int init (int argc, ACE_TCHAR *argv[]); + virtual int pre_run_test (Benchmark_Base *bp); + virtual int run_test (void); + virtual int post_run_test (void); + virtual int valid_test_object (Benchmark_Base *); +private: + int n_lwps_; + int orig_n_lwps_; +}; + +ACE_SVC_FACTORY_DECLARE (Performance_Test) + +#endif /* ACE_HAS_THREADS */ + +#endif /* ACE_PERFORMANCE_TEST_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.cpp new file mode 100644 index 00000000000..d2f5305de07 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.cpp @@ -0,0 +1,469 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "ace/OS_NS_strings.h" + +ACE_RCSID(Synch_Benchmarks, Performance_Test_Options, "$Id$") + +#if defined (ACE_HAS_THREADS) +// Manages the options. +Performance_Test_Options performance_test_options; + +size_t +Performance_Test_Options::count (void) +{ + size_t total = 0; + + if (performance_test_options.verbose ()) + ACE_DEBUG ((LM_DEBUG, "Thread work count size = %d\n", performance_test_options.thr_wc_size)); + + for (int i = 0; i < performance_test_options.thr_wc_size; i++) + { + if (performance_test_options.thr_work_count[i] != 0) + { + // if (performance_test_options.verbose ()) + ACE_DEBUG ((LM_DEBUG, "count[%d] = %d\n", i, performance_test_options.thr_work_count[i])); + total += performance_test_options.thr_work_count[i]; + } + } + + return total; +} + +void +Performance_Test_Options::init (void) +{ + for (int i = 0; i < this->thr_wc_size; i++) + this->thr_work_count[i] = 0; +} + +Performance_Test_Options::Performance_Test_Options (void) + : thr_wc_size (10000), + _service_entry (0), + _mapped_file (0), + _pipe_addr (const_cast<ACE_TCHAR *> (ACE_DEFAULT_RENDEZVOUS)), + _sleep_time (100), + _n_lwps (0), + _thr_count (4), + _t_flags (0), + _high_water_mark (8 * 1024), + _low_water_mark (1024), + _msg_size (128), + _initial_queue_length (0), + _logical_connections (1), + _physical_connections (1), + _iterations (100000), + _generate (0), + _udp (0), + _debugging (0), + _verbosity (0), + _ack (1), + _checksum (1), + _xdr (1), + _free_memory (1), + _zero_copy (0), + _print_summary (0), + _consecutive_ports (1), + _eager_exit (0) +{ + this->thr_work_count = new int[this->thr_wc_size]; + this->init (); +} + +void +Performance_Test_Options::parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("a:A:bBc:C:dDe:F:g:H:i:L:l:M:m:n:Np:P:s:S:t:T:uvX:Z:"), 0); + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'a': // Not used. (do_ack ???) + this->_ack = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'A': // Not used. (set rendezvous point.) + this->pipe_addr (get_opt.opt_arg ()); + break; + case 'B': // Create thread with THR_BOUND. + this->t_flags (THR_BOUND); + break; + case 'c': // Not used. (# of connections.) + { + long connections = ACE_OS::atoi (get_opt.opt_arg ()); + + if (connections < 0) + this->physical_connections (size_t (-connections)); + else if (connections > 0) + this->logical_connections (size_t (connections)); + else + ACE_DEBUG ((LM_WARNING, "warning, 0 connections!\n")); + + break; + } + case 'C': // Not used. (Toggle calculate checksum.) + this->_checksum = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'd': // Not used. (Enable debugging.) + this->_debugging = 1; + break; + case 'D': // Create thread with THR_DETACHED. + this->t_flags (THR_DETACHED); + break; + case 'e': // Perform eager exit (without cleaning up.) + this->_eager_exit = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'F': // Not used. + this->_free_memory = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'g': // Not used. (generate data ??) + this->_generate = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'H': // Not used. (set high water mark) + this->high_water_mark (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'i': // Not used. (# of iterations) + this->iterations (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'L': // Not used. (set low water mark) + this->low_water_mark (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'l': // Not used. (set initial queue length) + this->initial_queue_length (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'M': // Set message size in pipe_[proc|thr]_test. + this->msg_size (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'm': // Not used. (set mapped file name) + this->mapped_file (get_opt.opt_arg ()); + break; + case 'N': // Create thread with flag THR_NEW_LWP. + this->t_flags (THR_NEW_LWP); + break; + case 'n': // Set # of lwp's + this->n_lwps (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'p': // Toggle whether summary is printed. + this->_print_summary = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'P': // Not used. + this->consecutive_ports (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'S': // Not used (set service_entry ???) + this->service_entry (get_opt.opt_arg ()); + break; + case 's': // Set testing duration. + this->sleep_time (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'T': // Enable/disable tracing. +#if defined (ACE_HAS_TRACE) + if (ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0) + ACE_Trace::start_tracing (); + else if (ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("OFF")) == 0) + ACE_Trace::stop_tracing (); +#endif /* ACE_HAS_TRACE */ + break; + case 't': // Set # of threads contending the lock. + this->thr_count (ACE_OS::atoi (get_opt.opt_arg ())); + break; + case 'u': // Not used. (use udp.) + this->_udp = 1; + break; + case 'v': // Not used. (set display verbosely) + this->_verbosity = 1; + break; + case 'X': // Not used. (Use xdr conversion.) + this->_xdr = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + case 'Z': // Not used. (Do zero copy.) + this->_zero_copy = ACE_OS::strcasecmp (get_opt.opt_arg (), ACE_TEXT("ON")) == 0; + break; + default: + ACE_DEBUG ((LM_INFO, + "%s\n" + "\t[-a] (send acknowledgement)\n" + "\t[-A] address of pipe [%s]\n" + "\t[-B] (THR_BOUND)\n" + "\t[-c] + number of logical connections\n" + "\t[-c] - number of physical connections\n" + "\t[-C] (enable checksumming)\n" + "\t[-d] (enable debugging)\n" + "\t[-D] (THR_DETACHED)\n" + "\t[-e] (eager exit)\n" + "\t[-F] (free memory)\n" + "\t[-g] (generate data)\n" + "\t[-H] high water mark\n" + "\t[-i] number of test iterations [%d]\n" + "\t[-L] low water mark\n" + "\t[-m] mapped file\n" + "\t[-M] message size\n" + "\t[-n] number of LWPs\n" + "\t[-N] (THR_NEW_LWP)\n" + "\t[-p] (print benchmark summary)\n" + "\t[-P] number of consecutive ports\n" + "\t[-s] sleep time\n" + "\t[-S] service entry\n" + "\t[-t] number of threads [%d]\n" + "\t[-T] (enable tracing)\n" + "\t[-u] (UDP) \n" + "\t[-v] (verbose) \n" + "\t[-X] (enable xdr conversion)\n" + "\t[-Z] (enable zero-copy driver)\n%a", + argv[0], + this->pipe_addr (), + this->iterations (), + this->thr_count (), + 1)); + /* NOTREACHED */ + break; + } + + if (this->do_print_summary ()) + ACE_DEBUG ((LM_INFO, + "%8d = total iterations\n" + "%8d = logical connections\n" + "%8d = physical connections\n" + "%8d = message_size\n" + "%8d = calculated checksum\n" + "%8d = perform xdr conversion\n" + "%8d = number of LWPs requested\n" + "%8d = number of LWPs used\n", + this->iterations (), + this->logical_connections (), + this->physical_connections (), + this->msg_size (), + this->do_checksum () != 0, + this->do_xdr() != 0, + this->n_lwps (), + ACE_Thread::getconcurrency ())); + else if (this->verbose ()) + ACE_DEBUG ((LM_INFO, + "%8d = total iterations\n" + "%8d = logical connections\n" + "%8d = physical connections\n" + "%8d = thread count\n" + "%8d = low water mark\n" + "%8d = high water mark\n" + "%8d = message_size\n" + "%8d = initial queue length\n" + "%8d = consecutive ports\n" + "%8d = calculated checksum\n" + "%8d = perform xdr conversion\n" + "%8d = zero-copy driver\n" + "%8d = free dynamic memory\n" + "%8d = print summary only\n" + "%8d = eager exit\n" + "%8d = UDP\n" + "%8d = send ack\n" + "%8d = THR_DETACHED\n" + "%8d = THR_BOUND\n" + "%8d = THR_NEW_LWP\n" + "%8d = sleep time\n", + this->iterations (), + this->logical_connections (), + this->physical_connections (), + this->thr_count (), + this->low_water_mark (), + this->high_water_mark (), + this->msg_size (), + this->initial_queue_length (), + this->consecutive_ports (), + this->do_checksum () != 0, + this->do_xdr() != 0, + this->do_zero_copy () != 0, + this->do_delete () != 0, + this->do_print_summary () != 0, + this->do_eager_exit () != 0, + this->do_udp () != 0, + this->do_ack () != 0, + (this->t_flags () & THR_DETACHED) != 0, + (this->t_flags () & THR_BOUND) != 0, + (this->t_flags () & THR_NEW_LWP) != 0, + this->sleep_time ())); +} + +void +Performance_Test_Options::print_results (void) +{ + ACE_Profile_Timer::ACE_Elapsed_Time et; + this->_itimer.elapsed_time (et); + + ACE_Profile_Timer::Rusage rusage; + this->_itimer.elapsed_rusage (rusage); + + size_t total = this->count (); + double nbytes = total * this->msg_size (); + double cpu_time = et.user_time + et.system_time; + +#if 0 + mutex_timer.print_total ("ACE_Thread_Mutex overhead:", mutex_counter, 2); + condition_timer.print_total ("ACE_Condition overhead:", condition_counter, 2); + ACE_DEBUG ((LM_INFO, + "%8d (number of ACE_Thread_Mutex operations)\n" + "%8d (number of ACE_Condition operations)", + mutex_counter, condition_counter)); +#endif /* NDEBUG */ + + if (this->do_print_summary ()) + { +#if defined (ACE_HAS_PRUSAGE_T) + ACE_DEBUG ((LM_INFO, + "\n%8d PEs\n" + "%8.2f Mbit/sec\n" + "%8d (voluntary context switches)\n" + "%8d (involuntary context switches)\n" + "%8d (total context switches)\n" + "%8d.%d sec (wait-cpu time)\n" + "%8d.%d sec (user lock wait sleep time)\n" + "%8d.%d sec (all other sleep time)\n" + "%8d (major page faults)\n" + "%8d (minor page faults)\n" + "%8d (number of LWPs)\n", + this->thr_count (), + (nbytes / et.real_time) * 8.0 / 1024.0 / 1024.0, + rusage.pr_vctx, + rusage.pr_ictx, + rusage.pr_vctx + rusage.pr_ictx, + rusage.pr_wtime.tv_sec, rusage.pr_wtime.tv_nsec / 1000000, + rusage.pr_ltime.tv_sec, rusage.pr_ltime.tv_nsec / 1000000, + rusage.pr_slptime.tv_sec, rusage.pr_slptime.tv_nsec / 1000000, + rusage.pr_majf, + rusage.pr_minf, + ACE_Thread::getconcurrency ())); +#elif defined (ACE_HAS_GETRUSAGE) && !defined (ACE_WIN32) + ACE_DEBUG ((LM_INFO, + "\n%8d PEs\n" + "%8.2f Mbit/sec\n" + "%8d (voluntary context switches)\n" + "%8d (involuntary context switches)\n" + "%8d (total context switches)\n" + "%8d.%d sec (user time)\n" + "%8d.%d sec (system time)\n" + "%8d (major page faults)\n" + "%8d (minor page faults)\n" + "%8d (number of LWPs)\n", + this->thr_count (), + (nbytes / et.real_time) * 8.0 / 1024.0 / 1024.0, + rusage.ru_nvcsw, + rusage.ru_nivcsw, + rusage.ru_nvcsw + rusage.ru_nivcsw, + rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec / 1000000, + rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec / 1000000, + rusage.ru_majflt, + rusage.ru_minflt, + ACE_Thread::getconcurrency ())); +#elif defined (ACE_HAS_GETRUSAGE) && defined (ACE_WIN32) + // Need more stuff for Win32. + ACE_DEBUG ((LM_INFO, + "\n%8d PEs\n" + "%8.2f Mbit/sec\n" + "%8d (number of LWPs)\n", + this->thr_count (), + (nbytes / et.real_time) * 8.0 / 1024.0 / 1024.0, + ACE_Thread::getconcurrency ())); +#endif /* ACE_HAS_PRUSAGE_T */ + } + else + { + ACE_DEBUG ((LM_INFO, + "\ntotal work = %d\n" + "(Only interpret the next two statistics for throughput tests)\n" + "%f bytes in %.2f real seconds = %.2f Mbit/sec\n" + "%f bytes in %.2f CPU seconds = %.2f Mbit/sec\n", + total, + nbytes, et.real_time, (nbytes / et.real_time) * 8.0 / 1024.0 / 1024.0, + nbytes, cpu_time, (nbytes / cpu_time) * 8.0 / 1024.0 / 1024.0)); + +#if defined (ACE_HAS_PRUSAGE_T) + ACE_DEBUG ((LM_INFO, + "%8d = lwpid\n" + "%8d = lwp count\n" + "%8d = minor page faults\n" + "%8d = major page faults\n" + "%8d = input blocks\n" + "%8d = output blocks\n" + "%8d = messages sent\n" + "%8d = messages received\n" + "%8d = signals received\n" + "%8ds, %dms = wait-cpu (latency) time\n" + "%8ds, %dms = user lock wait sleep time\n" + "%8ds, %dms = all other sleep time\n" + "%8d = voluntary context switches\n" + "%8d = involuntary context switches\n" + "%8d = total context switches\n" + "%8d = system calls\n" + "%8d = chars read/written\n" + "%8d = number of LWPs\n" + "---------------------\n" + "real time = %.3f\n" + "user time = %.3f\n" + "system time = %.3f\n" + "---------------------\n", + rusage.pr_lwpid, + rusage.pr_count, + rusage.pr_minf, + rusage.pr_majf, + rusage.pr_inblk, + rusage.pr_oublk, + rusage.pr_msnd, + rusage.pr_mrcv, + rusage.pr_sigs, + rusage.pr_wtime.tv_sec, rusage.pr_wtime.tv_nsec / 1000000, + rusage.pr_ltime.tv_sec, rusage.pr_ltime.tv_nsec / 1000000, + rusage.pr_slptime.tv_sec, rusage.pr_slptime.tv_nsec / 1000000, + rusage.pr_vctx, + rusage.pr_ictx, + rusage.pr_vctx + rusage.pr_ictx, + rusage.pr_sysc, + rusage.pr_ioch, + ACE_Thread::getconcurrency (), + et.real_time, et.user_time, et.system_time)); +#elif defined (ACE_HAS_GETRUSAGE) && !defined (ACE_WIN32) + ACE_DEBUG ((LM_INFO, + "%8d = minor page faults\n" + "%8d = major page faults\n" + "%8d = input blocks\n" + "%8d = output blocks\n" + "%8d = messages sent\n" + "%8d = messages received\n" + "%8d = signals received\n" + "%8d = voluntary context switches\n" + "%8d = involuntary context switches\n" + "%8d = total context switches\n" + "%8d = number of LWPs\n" + "---------------------\n" + "real time = %.3f\n" + "user time = %.3f\n" + "system time = %.3f\n" + "---------------------\n", + rusage.ru_minflt, + rusage.ru_majflt, + rusage.ru_inblock, + rusage.ru_oublock, + rusage.ru_msgsnd, + rusage.ru_msgrcv, + rusage.ru_nsignals, + rusage.ru_nvcsw, + rusage.ru_nivcsw, + rusage.ru_nvcsw + rusage.ru_nivcsw, + ACE_Thread::getconcurrency (), + et.real_time, et.user_time, et.system_time)); +#elif defined (ACE_HAS_GETRUSAGE) && defined (ACE_WIN32) + // need to write more dump ops for rusage on Win32 + ACE_DEBUG ((LM_INFO, + "%8d = number of LWPs\n" + "---------------------\n" + "real time = %.3f\n" + "user time = %.3f\n" + "system time = %.3f\n" + "---------------------\n", + ACE_Thread::getconcurrency (), + et.real_time, et.user_time, et.system_time)); +#endif /* ACE_HAS_PRUSAGE_T */ + } + if (performance_test_options.do_eager_exit ()) + ACE_OS::_exit (0); +} +#endif /* ACE_HAS_THREADS */ + diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.h b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.h new file mode 100644 index 00000000000..b4c53e30109 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.h @@ -0,0 +1,133 @@ +/* -*- C++ -*- */ +// $Id$ + +// Option manager for performance tests. + +#ifndef _PERFORMANCE_TEST_OPTIONS_H +#define _PERFORMANCE_TEST_OPTIONS_H + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/Profile_Timer.h" +#include "ace/Log_Msg.h" +#include "ace/Thread_Manager.h" +#include "ace/Atomic_Op.h" +#include "ace/svc_export.h" + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Performance_Test_Options +{ +public: + Performance_Test_Options (void); + void parse_args (int argc, ACE_TCHAR *argv[]); + + void init (void); + + void start_timer (void); + void stop_timer (void); + + void thr_count (size_t count); + size_t thr_count (void); + + void pipe_addr (ACE_TCHAR pipe[]); + ACE_TCHAR *pipe_addr (void); + + void mapped_file (ACE_TCHAR filename[]); + ACE_TCHAR *mapped_file (void); + + void service_entry (ACE_TCHAR *service_entry); + ACE_TCHAR *service_entry (void); + + void sleep_time (size_t count); + size_t sleep_time (void); + + void logical_connections (size_t count); + size_t logical_connections (void); + + void physical_connections (size_t count); + size_t physical_connections (void); + + void consecutive_ports (size_t count); + size_t consecutive_ports (void); + + void initial_queue_length (size_t length); + size_t initial_queue_length (void); + + void high_water_mark (size_t size); + size_t high_water_mark (void); + + void low_water_mark (size_t size); + size_t low_water_mark (void); + + void msg_size (size_t size); + size_t msg_size (void); + + void iterations (size_t n); + size_t iterations (void); + + void n_lwps (size_t n); + size_t n_lwps (void); + + void t_flags (long flag); + long t_flags (void); + + size_t count (void); + + int debug (void); + int verbose (void); + int do_checksum (void); + int do_generate (void); + int do_ack (void); + int do_delete (void); + int do_eager_exit (void); + int do_print_summary (void); + int do_udp (void); + int do_xdr (void); + int do_zero_copy (void); + void print_results (void); + + ACE_Atomic_Op<ACE_Thread_Mutex, size_t> msg_count; // Keep track of number of messages atomically. + int *thr_work_count; // Count activity per-thread. + int thr_wc_size; // Max number of threads. + +private: + ACE_Profile_Timer _itimer; // Keep track of time. + ACE_TCHAR *_service_entry; // Name of the shared object file and shared object. + ACE_TCHAR *_mapped_file; // Name of the mapped file. + ACE_TCHAR *_pipe_addr; // Name of the STREAM pipe. + size_t _sleep_time; // Time to sleep. + size_t _n_lwps; // Number of LWPs. + size_t _thr_count; // Number of threads to spawn. + long _t_flags; // Flags to thr_create(). + size_t _high_water_mark; // ACE_Queue high water mark. + size_t _low_water_mark; // ACE_Queue low water mark. + size_t _msg_size; // Size of a message. + size_t _initial_queue_length; // Initial number of items in the queue. + size_t _logical_connections; // Number of logical connections. + size_t _physical_connections; // Number of physical connections. + size_t _iterations; // Number of iterations to run the test program. + int _generate; // Generate the data. + int _udp; // Use UDP format. + int _debugging; // Extra debugging info. + int _verbosity; // Extra verbose messages. + int _ack; // Do an acknowledgement. + int _checksum; // Is checksumming enabled?. + int _xdr; // Is xdr conversion enabled?. + int _free_memory; // Are we freeing up memory?. + int _zero_copy; // Implement a zero-copy driver?. + int _print_summary; // Print a summary of the results only. + size_t _consecutive_ports; // Number of consecutive messages from same port. + int _eager_exit; // Exit eagerly, without cleaning up. +}; + +// Make this available to any code that wants to see it! +extern ACE_Svc_Export Performance_Test_Options performance_test_options; + +#include "Performance_Test_Options.inl" +#endif /* ACE_HAS_THREADS */ +#endif /* _PERFORMANCE_TEST_OPTIONS_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.inl b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.inl new file mode 100644 index 00000000000..6856edfa3cd --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Performance_Test_Options.inl @@ -0,0 +1,269 @@ +/* -*- C++ -*- */ +// $Id$ + +/* Option manager for ustreams */ + +#include "ace/Get_Opt.h" + +// Since this is only included in the .h file, these should be +// remain inline, not ACE_INLINE. +// FUZZ: disable check_for_inline + + +inline int +Performance_Test_Options::do_print_summary (void) +{ + return this->_print_summary; +} + +inline int +Performance_Test_Options::do_udp (void) +{ + return this->_udp; +} + +inline void +Performance_Test_Options::start_timer (void) +{ + this->_itimer.start (); +} + +inline void +Performance_Test_Options::stop_timer (void) +{ + this->_itimer.stop (); +} + +inline int +Performance_Test_Options::do_generate (void) +{ + return this->_generate; +} + +inline int +Performance_Test_Options::do_ack (void) +{ + return this->_ack; +} + +inline int +Performance_Test_Options::do_eager_exit (void) +{ + return this->_eager_exit; +} + +inline int +Performance_Test_Options::do_zero_copy (void) +{ + return this->_zero_copy; +} + +inline int +Performance_Test_Options::do_checksum (void) +{ + return this->_checksum; +} + +inline int +Performance_Test_Options::do_delete (void) +{ + return this->_free_memory; +} + +inline int +Performance_Test_Options::do_xdr (void) +{ + return this->_xdr; +} + +inline void +Performance_Test_Options::n_lwps (size_t count) +{ + this->_n_lwps = count; +} + +inline size_t +Performance_Test_Options::n_lwps (void) +{ + return this->_n_lwps; +} + +inline void +Performance_Test_Options::pipe_addr (ACE_TCHAR *pipe) +{ + this->_pipe_addr = pipe; +} + +inline ACE_TCHAR * +Performance_Test_Options::pipe_addr (void) +{ + return this->_pipe_addr; +} + +inline void +Performance_Test_Options::service_entry (ACE_TCHAR *pipe) +{ + this->_service_entry = pipe; +} + +inline ACE_TCHAR * +Performance_Test_Options::service_entry (void) +{ + return this->_service_entry; +} + +inline void +Performance_Test_Options::mapped_file (ACE_TCHAR *filename) +{ + this->_mapped_file = filename; +} + +inline ACE_TCHAR * +Performance_Test_Options::mapped_file (void) +{ + return this->_mapped_file; +} + +inline void +Performance_Test_Options::sleep_time (size_t count) +{ + this->_sleep_time = count; +} + +inline size_t +Performance_Test_Options::sleep_time (void) +{ + return this->_sleep_time; +} + +inline void +Performance_Test_Options::thr_count (size_t count) +{ + this->_thr_count = count; +} + +inline size_t +Performance_Test_Options::thr_count (void) +{ + return this->_thr_count; +} + +inline void +Performance_Test_Options::consecutive_ports (size_t count) +{ + this->_consecutive_ports = count; +} + +inline size_t +Performance_Test_Options::consecutive_ports (void) +{ + return this->_consecutive_ports; +} + +inline void +Performance_Test_Options::logical_connections (size_t count) +{ + this->_logical_connections = count; +} + +inline size_t +Performance_Test_Options::logical_connections (void) +{ + return this->_logical_connections; +} + +inline void +Performance_Test_Options::physical_connections (size_t count) +{ + this->_physical_connections = count; +} + +inline size_t +Performance_Test_Options::physical_connections (void) +{ + return this->_physical_connections; +} + +inline void +Performance_Test_Options::initial_queue_length (size_t length) +{ + this->_initial_queue_length = length; +} + +inline size_t +Performance_Test_Options::initial_queue_length (void) +{ + return this->_initial_queue_length; +} + +inline void +Performance_Test_Options::high_water_mark (size_t size) +{ + this->_high_water_mark = size; +} + +inline size_t +Performance_Test_Options::high_water_mark (void) +{ + return this->_high_water_mark; +} + +inline void +Performance_Test_Options::low_water_mark (size_t size) +{ + this->_low_water_mark = size; +} + +inline size_t +Performance_Test_Options::low_water_mark (void) +{ + return this->_low_water_mark; +} + +inline void +Performance_Test_Options::msg_size (size_t size) +{ + this->_msg_size = size; +} + +inline size_t +Performance_Test_Options::msg_size (void) +{ + return this->_msg_size; +} + +inline void +Performance_Test_Options::iterations (size_t n) +{ + this->_iterations = n; +} + +inline size_t +Performance_Test_Options::iterations (void) +{ + return this->_iterations; +} + +inline void +Performance_Test_Options::t_flags (long flag) +{ + this->_t_flags |= flag; +} + +inline long +Performance_Test_Options::t_flags (void) +{ + return this->_t_flags; +} + +inline int +Performance_Test_Options::debug (void) +{ + return this->_debugging; +} + +inline int +Performance_Test_Options::verbose (void) +{ + return this->_verbosity; +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/README b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/README new file mode 100644 index 00000000000..28f1f160e8a --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/README @@ -0,0 +1,76 @@ +The files in this directory support controlled benchmarking of the ACE +synchronization mechanisms. + + These mechanisms include: + + . Mutexes + . Reader/writer locks + . Condition variables + . Semaphores + . Tokens + . Adaptive lockings + +There are additional tests that measure the memory bandwidth under the +following conditions: + + . User memory-to-memory copying of data within a single thread + . User memory-to-kernel-to-user memory copying via pipes + between separate processes, as well as between separate + threads in the same process + +There are many options available for this module that can be put into +svc.conf files. See the Performance_Test_Options.[Chi] file for more +details. Some reasonable options to use to run the tests are: + + -v -B -s 15 + -v -n 4 -t 4 -s 15 + +You should experiment with other options as you see fit. Note that on +Solaris, you should always make sure that you have more than 1 LWP (by +using either the -B or the -n options) since otherwise the program may +get into an infinite loop due to the semantics of SunOS unbound +threads... (This may no longer be the case.) + + +Available Options in Performance_Test module: +============================================= + +Thread Creation: +---------------- + -B: Create thread with THR_BOUND + -D: Create thread with THR_DETACHED + -N: Create thread with flag THR_NEW_LWP + -n: Set # of lwp's (default is 0) + -t: Set # of threads contending the lock (default is 4) + +Run Test: +--------- + -s: Set testing duration (in seconds, default is 100) + -T: Enable/disable tracing. + +Misc: +----- + -p: Toggle whether summary is printed + -e: Perform eager exit (without cleaning up) + -M: Set message size in pipe_[proc|thr]_test + +Reserved Flags: +--------------- + -a: Not used. (do_ack ???) + -A: Not used. (set rendezvous point) + -c: Not used. (# of connections) + -C: Not used. (Toggle calculate checksum) + -d: Not used. (Enable debugging) + -F: Not used. (Free memory) + -g: Not used. (generate data ??) + -H: Not used. (set high water mark) + -i: Not used. (# of iterations) + -L: Not used. (set low water mark) + -l: Not used. (set initial queue length) + -m: Not used. (set mapped file name) + -P: Not used. (set consecutive ports) + -S: Not used. (set service_entry ???) + -u: Not used. (use udp) + -v: Not used. (set display verbosely) + -X: Not used. (Use xdr conversion) + -Z: Not used. (Do zero copy) diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Synch_Benchmarks_Perf_Test.mpc b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Synch_Benchmarks_Perf_Test.mpc new file mode 100644 index 00000000000..a76c10f94ee --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/Synch_Benchmarks_Perf_Test.mpc @@ -0,0 +1,16 @@ +// -*- MPC -*- +// $Id$ + +project : acelib { + sharedname = Perf_Test + avoids += ace_for_tao + after += Synch_Lib + libs += Synch_Lib + libpaths += ../Synch_Lib + + specific (automake) { + includes += $(srcdir)/.. + } else { + includes += .. + } +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_mutex_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_mutex_test.cpp new file mode 100644 index 00000000000..d438e9ac3f4 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_mutex_test.cpp @@ -0,0 +1,32 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Log_Msg.h" +#include "Adaptive_Lock_Performance_Test_Base.h" +#include "ace/Lock_Adapter_T.h" + +ACE_RCSID(Synch_Benchmarks, adaptive_mutex_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Adaptive_Mutex_Test : public Adaptive_Lock_Performance_Test_Base +{ +public: + virtual int init (int, ACE_TCHAR *[]); +}; + +int +Adaptive_Mutex_Test::init (int, ACE_TCHAR *[]) +{ + ACE_Lock *lock; + ACE_NEW_RETURN (lock, + ACE_Lock_Adapter<ACE_Thread_Mutex> (), + -1); + + return this->set_lock (lock); +} + +ACE_SVC_FACTORY_DECLARE (Adaptive_Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Adaptive_Mutex_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_recursive_lock_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_recursive_lock_test.cpp new file mode 100644 index 00000000000..cc49bf1b698 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_recursive_lock_test.cpp @@ -0,0 +1,33 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Log_Msg.h" +#include "Adaptive_Lock_Performance_Test_Base.h" +#include "ace/Lock_Adapter_T.h" +#include "ace/Recursive_Thread_Mutex.h" + +ACE_RCSID(Synch_Benchmarks, adaptive_recursive_lock_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Adaptive_Recursive_Lock_Test : public Adaptive_Lock_Performance_Test_Base +{ +public: + virtual int init (int, ACE_TCHAR *[]); +}; + +int +Adaptive_Recursive_Lock_Test::init (int, ACE_TCHAR *[]) +{ + ACE_Lock *lock; + ACE_NEW_RETURN (lock, + ACE_Lock_Adapter<ACE_Recursive_Thread_Mutex> (), + -1); + + return this->set_lock (lock); +} + +ACE_SVC_FACTORY_DECLARE (Adaptive_Recursive_Lock_Test) +ACE_SVC_FACTORY_DEFINE (Adaptive_Recursive_Lock_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_sema_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_sema_test.cpp new file mode 100644 index 00000000000..2644aef7415 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/adaptive_sema_test.cpp @@ -0,0 +1,38 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Log_Msg.h" +#include "Adaptive_Lock_Performance_Test_Base.h" +#include "ace/Semaphore.h" +#include "ace/Lock_Adapter_T.h" + +ACE_RCSID(Synch_Benchmarks, adaptive_sema_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Adaptive_Sema_Test : public Adaptive_Lock_Performance_Test_Base +{ +public: + virtual int init (int, ACE_TCHAR *[]); + +private: + static ACE_Semaphore sema; +}; + +ACE_Semaphore Adaptive_Sema_Test::sema (1); + +int +Adaptive_Sema_Test::init (int, ACE_TCHAR *[]) +{ + ACE_Lock *lock; + ACE_NEW_RETURN (lock, + ACE_Lock_Adapter<ACE_Semaphore> (Adaptive_Sema_Test::sema), + -1); + + return this->set_lock (lock); +} + +ACE_SVC_FACTORY_DECLARE (Adaptive_Sema_Test) +ACE_SVC_FACTORY_DEFINE (Adaptive_Sema_Test) + +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/condb_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/condb_test.cpp new file mode 100644 index 00000000000..9c0a09534e2 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/condb_test.cpp @@ -0,0 +1,69 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, condb_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Cond_Brdcast_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Thread_Mutex mutex; + static int resources; + + static ACE_Condition_Thread_Mutex notfull; + static ACE_Condition_Thread_Mutex notempty; +}; + +ACE_Thread_Mutex Cond_Brdcast_Test::mutex; +int Cond_Brdcast_Test::resources; +ACE_Condition_Thread_Mutex Cond_Brdcast_Test::notfull (Cond_Brdcast_Test::mutex); +ACE_Condition_Thread_Mutex Cond_Brdcast_Test::notempty (Cond_Brdcast_Test::mutex); + +int +Cond_Brdcast_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + // Special case for first thread... + if (ni == 4) + while (!this->done ()) + { + mutex.acquire (); + while (resources > 0) + notfull.wait (); + performance_test_options.thr_work_count[ni]++; + resources = performance_test_options.thr_count () - 1; + buffer++; + notempty.broadcast (); + mutex.release (); + } + else + while (!this->done ()) + { + mutex.acquire (); + while (resources == 0) + notempty.wait (); + performance_test_options.thr_work_count[ni]++; + buffer++; + if (--resources == 0) + notfull.signal (); + mutex.release (); + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Cond_Brdcast_Test) +ACE_SVC_FACTORY_DEFINE (Cond_Brdcast_Test) + +// ACE_Service_Object_Type cbt (&cond_brdcast_test, "Condition_Broadcast_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/conds_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/conds_test.cpp new file mode 100644 index 00000000000..a30b67c040f --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/conds_test.cpp @@ -0,0 +1,72 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, conds_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Cond_Signal_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Thread_Mutex mutex; + static int resources; + + static ACE_Condition_Thread_Mutex notfull; + static ACE_Condition_Thread_Mutex notempty; +}; + +ACE_Thread_Mutex Cond_Signal_Test::mutex; +int Cond_Signal_Test::resources; +ACE_Condition_Thread_Mutex Cond_Signal_Test::notfull (Cond_Signal_Test::mutex); +ACE_Condition_Thread_Mutex Cond_Signal_Test::notempty (Cond_Signal_Test::mutex); + +int +Cond_Signal_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + // This is a horrible hack and only works for Solaris threads. This + // clearly needs to change... + if (ni == 4) + while (!this->done ()) + { + mutex.acquire (); + + while (resources > 0) + notfull.wait (); + + performance_test_options.thr_work_count[ni]++; + resources = performance_test_options.thr_count () - 1; + buffer++; + notempty.signal (); + mutex.release (); + } + else + while (!this->done ()) + { + mutex.acquire (); + while (resources == 0) + notempty.wait (); + performance_test_options.thr_work_count[ni]++; + buffer++; + if (--resources == 0) + notfull.signal (); + mutex.release (); + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Cond_Signal_Test) +ACE_SVC_FACTORY_DEFINE (Cond_Signal_Test) + +// ACE_Service_Object_Type cst (&cond_signal_test, "Condition_Signal_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/context_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/context_test.cpp new file mode 100644 index 00000000000..fc4fa0dc26d --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/context_test.cpp @@ -0,0 +1,38 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, context_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Context_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); +}; + +int +Context_Test::svc (void) +{ + int ni = this->thr_id (); + + synch_count = 1; + + while (!this->done ()) + { + ACE_Thread::yield (); + performance_test_options.thr_work_count[ni]++; + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Context_Test) +ACE_SVC_FACTORY_DEFINE (Context_Test) + +// ACE_Service_Object_Type ct (&context_test, "Context_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/guard_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/guard_test.cpp new file mode 100644 index 00000000000..b5296452322 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/guard_test.cpp @@ -0,0 +1,126 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Guard_T.h" +#include "ace/Log_Msg.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, mutex_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Guard_Test : public Benchmark_Performance +{ +public: + enum + { // svc.conf options + TEST_ACE_GUARD, // -g + TEST_ACE_THREAD_MUTEX_GUARD // -t + }; + + virtual int svc (void); + virtual int init (int, ACE_TCHAR *[]); + + void test_guard (int); +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) + void test_thread_guard (int); +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ +private: + static int guard_type_; + static ACE_Thread_Mutex mutex_; +}; + +int Guard_Test::guard_type_ = Guard_Test::TEST_ACE_GUARD; +ACE_Thread_Mutex Guard_Test::mutex_; + +int +Guard_Test::init (int argc, ACE_TCHAR *argv[]) +{ + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("gt")); + int c; + + while ((c = getopt()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch (c) + { +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) + case 't': + Guard_Test::guard_type_ = Guard_Test::TEST_ACE_THREAD_MUTEX_GUARD; + break; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + case 'g': + Guard_Test::guard_type_ = Guard_Test::TEST_ACE_GUARD; + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, + "Invalid option\n"), -1); + } + } + return 0; +} + +int +Guard_Test::svc (void) +{ + // Extract out the unique thread-specific value to be used as an + // index... + int ni = this->thr_id (); + synch_count = 2; + + switch (Guard_Test::guard_type_) + { + case Guard_Test::TEST_ACE_GUARD: + this->test_guard (ni); + break; +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) + case Guard_Test::TEST_ACE_THREAD_MUTEX_GUARD: + this->test_thread_guard (ni); + break; +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ + default: + return -1; + } + return 0; +} + +void +Guard_Test::test_guard (int ni) +{ + while (!this->done ()) + { + { + ACE_GUARD (ACE_Thread_Mutex, _ace_mon, Guard_Test::mutex_); + + performance_test_options.thr_work_count[ni]++; + buffer++; + } + } +} + +#if defined (ACE_USES_OBSOLETE_GUARD_CLASSES) +# define ACE_THREAD_GUARD(OBJ,LOCK) \ + ACE_Thread_Mutex_Guard OBJ (LOCK); \ + if (OBJ.locked () == 0) return; + +void +Guard_Test::test_thread_guard (int ni) +{ + while (!this->done ()) + { + { + ACE_THREAD_GUARD (_ace_mon, Guard_Test::mutex_); + + performance_test_options.thr_work_count[ni]++; + buffer++; + } + } +} +#endif /* ACE_USES_OBSOLETE_GUARD_CLASSES */ +ACE_SVC_FACTORY_DECLARE (Guard_Test) +ACE_SVC_FACTORY_DEFINE (Guard_Test) + +// ACE_Service_Object_Type mut (&mutex_test, "Guard_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/memory_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/memory_test.cpp new file mode 100644 index 00000000000..ff62b5e3a2a --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/memory_test.cpp @@ -0,0 +1,42 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/OS_NS_string.h" +#include "ace/Log_Msg.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, memory_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Memory_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); +}; + +int +Memory_Test::svc (void) +{ + int ni = this->thr_id (); + size_t length = performance_test_options.msg_size (); + char *from = new char[length]; + char *to = new char[length]; + + synch_count = 1; + + while (!this->done ()) + { + ACE_OS::memcpy (to, from, length); + performance_test_options.thr_work_count[ni]++; + } + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Memory_Test) +ACE_SVC_FACTORY_DEFINE (Memory_Test) + +// ACE_Service_Object_Type mt (&memory_test, "Memory_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/mutex_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/mutex_test.cpp new file mode 100644 index 00000000000..c1e0a1e0ad0 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/mutex_test.cpp @@ -0,0 +1,45 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, mutex_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Mutex_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Thread_Mutex mutex; +}; + +ACE_Thread_Mutex Mutex_Test::mutex; + +int +Mutex_Test::svc (void) +{ + // Extract out the unique thread-specific value to be used as an + // index... + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + mutex.acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + mutex.release (); + } + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Mutex_Test) +ACE_SVC_FACTORY_DEFINE (Mutex_Test) + +// ACE_Service_Object_Type mut (&mutex_test, "Mutex_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_proc_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_proc_test.cpp new file mode 100644 index 00000000000..10bed9b88f3 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_proc_test.cpp @@ -0,0 +1,86 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_unistd.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, pipe_proc_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Pipe_Proc_Test : public Benchmark_Performance +{ +public: + int init (int, ACE_TCHAR **); + virtual int svc (void); + +private: + ACE_HANDLE pipe_handles[2]; + + void reader (ACE_HANDLE handle); +}; + +int +Pipe_Proc_Test::init (int, ACE_TCHAR **) +{ + synch_count = 1; + + if (ACE_OS::pipe (this->pipe_handles) == -1) + ACE_OS::perror (ACE_TEXT("pipe")), ACE_OS::exit (1); + + switch (ACE_OS::fork ()) + { + case -1: + ACE_OS::perror (ACE_TEXT("fork")), ACE_OS::exit (1); + case 0: + this->reader (pipe_handles[0]); + /* NOTREACHED */ + break; + default: + break; + } + return 1; +} + +void +Pipe_Proc_Test::reader (ACE_HANDLE handle) +{ + int ni = this->thr_id (); + int length = performance_test_options.msg_size (); + char *to; + + ACE_NEW (to, char[length]); + + while (ACE_OS::read (handle, to, length) > 0) + performance_test_options.thr_work_count[ni]++; +} + + +int +Pipe_Proc_Test::svc (void) +{ + ssize_t length = performance_test_options.msg_size (); + int ni = this->thr_id (); + ACE_HANDLE handle = this->pipe_handles[1]; + char *from; + + ACE_NEW_RETURN (from, char[length], -1); + + while (!this->done ()) + if (ACE_OS::write (handle, from, length) == length) + performance_test_options.thr_work_count[ni]++; + else + ACE_OS::perror (ACE_TEXT("write")); + + ACE_OS::close (this->pipe_handles[0]); + ACE_OS::close (this->pipe_handles[1]); + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Pipe_Proc_Test) +ACE_SVC_FACTORY_DEFINE (Pipe_Proc_Test) + +// ACE_Service_Object_Type ppt (&pipe_proc_test, "Pipe_Proc_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_thr_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_thr_test.cpp new file mode 100644 index 00000000000..895b5a846fe --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/pipe_thr_test.cpp @@ -0,0 +1,79 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Thread_Manager.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, pipe_thr_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Pipe_Thr_Test : public Benchmark_Performance +{ +public: + virtual int init (int, ACE_TCHAR **); + virtual int svc (void); + +private: + ACE_HANDLE pipe_handles[2]; + + static void *reader (Pipe_Thr_Test *); +}; + +void * +Pipe_Thr_Test::reader (Pipe_Thr_Test *t) +{ + ACE_HANDLE handle = t->pipe_handles[0]; + int ni = t->thr_id (); + size_t length = performance_test_options.msg_size (); + char *to; + ACE_NEW_RETURN (to, char[length], 0); + + while (ACE_OS::read (handle, to, length) > 0) + performance_test_options.thr_work_count[ni]++; + + return 0; +} + +int +Pipe_Thr_Test::init (int, ACE_TCHAR **) +{ + synch_count = 1; + + if (ACE_OS::pipe (this->pipe_handles) == -1) + ACE_OS::perror (ACE_TEXT("pipe")), ACE_OS::exit (1); + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (Pipe_Thr_Test::reader), + (void *) this, performance_test_options.t_flags ()) == -1) + ACE_OS::perror (ACE_TEXT("thr_create")), ACE_OS::exit (1); + + return 1; +} + +int +Pipe_Thr_Test::svc (void) +{ + ssize_t length = performance_test_options.msg_size (); + ACE_HANDLE handle = this->pipe_handles[1]; + char *from; + ACE_NEW_RETURN (from, char[length], -1); + + while (!this->done ()) + if (ACE_OS::write (handle, from, length) != length) + ACE_OS::perror (ACE_TEXT("write")); + + ACE_OS::close (this->pipe_handles[0]); + ACE_OS::close (this->pipe_handles[1]); + + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Pipe_Thr_Test) +ACE_SVC_FACTORY_DEFINE (Pipe_Thr_Test) + +// ACE_Service_Object_Type ptt (&pipe_thr_test, "Pipe_Thr_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/recursive_lock_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/recursive_lock_test.cpp new file mode 100644 index 00000000000..6246835ee88 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/recursive_lock_test.cpp @@ -0,0 +1,44 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Recursive_Thread_Mutex.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, recursive_lock_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Recursive_Lock_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Recursive_Thread_Mutex mutex; +}; + +ACE_Recursive_Thread_Mutex Recursive_Lock_Test::mutex; + +int +Recursive_Lock_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + this->mutex.acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + this->mutex.release (); + } + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Recursive_Lock_Test) +ACE_SVC_FACTORY_DEFINE (Recursive_Lock_Test) + +// ACE_Service_Object_Type rlt (&recursive_lock_test, "Recursive_Lock_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwrd_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwrd_test.cpp new file mode 100644 index 00000000000..a0eb5c6cff8 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwrd_test.cpp @@ -0,0 +1,45 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/RW_Mutex.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, rwrd_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export RWRD_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_RW_Mutex rw_lock; +}; + +ACE_RW_Mutex RWRD_Test::rw_lock; + +int +RWRD_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + rw_lock.acquire_read (); + performance_test_options.thr_work_count[ni]++; + buffer++; + rw_lock.release (); + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (RWRD_Test) +ACE_SVC_FACTORY_DEFINE (RWRD_Test) + +// ACE_Service_Object_Type rwrdt (&rwrd_test, "RWRD_Mutex_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwwr_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwwr_test.cpp new file mode 100644 index 00000000000..1c7a25eae1f --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/rwwr_test.cpp @@ -0,0 +1,45 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/RW_Mutex.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, rwwr_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export RWWR_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_RW_Mutex rw_lock; +}; + +ACE_RW_Mutex RWWR_Test::rw_lock; + +int +RWWR_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + rw_lock.acquire_write (); + performance_test_options.thr_work_count[ni]++; + buffer++; + rw_lock.release (); + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (RWWR_Test) +ACE_SVC_FACTORY_DEFINE (RWWR_Test) + +// ACE_Service_Object_Type rwwrt (&rwwr_test, "RWWR_Mutext_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sema_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sema_test.cpp new file mode 100644 index 00000000000..9f1d7657139 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sema_test.cpp @@ -0,0 +1,45 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" +#include "ace/Semaphore.h" + +ACE_RCSID(Synch_Benchmarks, sema_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Sema_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Semaphore sema; +}; + +ACE_Semaphore Sema_Test::sema (1); + +int +Sema_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + sema.acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + sema.release (); + } + + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Sema_Test) +ACE_SVC_FACTORY_DEFINE (Sema_Test) + +// ACE_Service_Object_Type semt (&sema_test, "Semaphore_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sysvsema_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sysvsema_test.cpp new file mode 100644 index 00000000000..5899e4f1a75 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/sysvsema_test.cpp @@ -0,0 +1,67 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/SV_Semaphore_Simple.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, sysvsema_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export SYSVSema_Test : public Benchmark_Performance +{ +public: + virtual int init (int, ACE_TCHAR *[]); + virtual int fini (void); + virtual int svc (void); + +private: + static ACE_SV_Semaphore_Simple *sema; +}; + + +ACE_SV_Semaphore_Simple *SYSVSema_Test::sema = 0; + +int +SYSVSema_Test::init (int, ACE_TCHAR *[]) +{ +#if defined (ACE_HAS_SYSV_IPC) + ACE_NEW_RETURN (SYSVSema_Test::sema, ACE_SV_Semaphore_Simple ((key_t) 1234), -1); + return 0; +#else + ACE_ERROR_RETURN ((LM_ERROR, "SysV Semaphore not supported on this platform.\n"), -1); +#endif /* ACE_HAS_SYSV_IPC */ +} + +int +SYSVSema_Test::fini (void) +{ + delete SYSVSema_Test::sema; + return 0; +} + +int +SYSVSema_Test::svc (void) +{ + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + SYSVSema_Test::sema->acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + SYSVSema_Test::sema->release (); + } + + SYSVSema_Test::sema->remove (); + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (SYSVSema_Test) +ACE_SVC_FACTORY_DEFINE (SYSVSema_Test) + +// ACE_Service_Object_Type st (&sysvsema_test, "SYSVSema_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Perf_Test/token_test.cpp b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/token_test.cpp new file mode 100644 index 00000000000..fe66740feca --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Perf_Test/token_test.cpp @@ -0,0 +1,46 @@ +// $Id$ + +#define ACE_BUILD_SVC_DLL +#include "ace/Token.h" +#include "Performance_Test_Options.h" +#include "Benchmark_Performance.h" + +ACE_RCSID(Synch_Benchmarks, token_test, "$Id$") + +#if defined (ACE_HAS_THREADS) + +class ACE_Svc_Export Token_Test : public Benchmark_Performance +{ +public: + virtual int svc (void); + +private: + static ACE_Token token; +}; + +ACE_Token Token_Test::token; + +int +Token_Test::svc (void) +{ + // Extract out the unique thread-specific value to be used as an + // index... + int ni = this->thr_id (); + synch_count = 2; + + while (!this->done ()) + { + token.acquire (); + performance_test_options.thr_work_count[ni]++; + buffer++; + token.release (); + } + /* NOTREACHED */ + return 0; +} + +ACE_SVC_FACTORY_DECLARE (Token_Test) +ACE_SVC_FACTORY_DEFINE (Token_Test) + +// ACE_Service_Object_Type tok (&token_test, "Token_Test"); +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/README b/ACE/performance-tests/Synch-Benchmarks/README new file mode 100644 index 00000000000..c902ecab244 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/README @@ -0,0 +1,46 @@ +This directory contains a program for benchmarking various +synchronization and concurrent programming mechanisms in ACE. + +To build the program, do a make on the top level Makefile (or, +on NT, open Synch_Tests.dsw and build evey project in it.) +There are several modules which perform different benchmarking +measurements in subdirectories. Here is a short description of all +the subdirectories: + + Synch_lib: This directory contains interface definitions required + by both main program (synch_driver) and other modules. + Base_Test: This directory contains a set of baseline tests. They + measure the time to obtain a lock without contension, + or trying to objtain a lock with contension. + Perf_Test: This directory contains a set of performance tests. + They measure the opeation performed by spawning a + bunch of threads and let these threads compete with + each other in obtaining a lock. + +There'll always be a "Method" object in each module subdirectory. +They defines the method used in benchmarking and usually takes some +options which can be defined in the 'svc.conf' file. Beside from this +"Method" object, there are also a lot of "Test" objects that define +the mechanism to be tested. + +Executing the program: the synch_driver performs various benchmarking +according to the "script" defined in 'svc.conf' file. All command +line options applicable to ACE's Service_Config class can be used. + +The entries in svc.conf file are always in groups. Each group +consists of several svc.conf entries. The first entry in each group +is the "Method" entry which defines the methodology used in the test. +The rest of the entries are "Test" entries which define the mechanism +to be test using the "Method". + +At this moment, you can not have entries with the same object (either +"Method" object or "Test" object) in one svc.conf file. Therefore, at +this moment, there can only be two groups in the svc.conf. Of them, +each "Test" can only be performed once. Therefore, there's no way to +benchmark using the same method with differnt configurations within +the same svc.conf file. However, this can be easily overcome by +defining multiple svc.conf files and invoking the synch_driver with +different svc.conf file. + +There'll be more detailed description about how a "Method" object can +be configured in respective subdirectory.
\ No newline at end of file diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Benchmarks.mpc b/ACE/performance-tests/Synch-Benchmarks/Synch_Benchmarks.mpc new file mode 100644 index 00000000000..92ad3685637 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Benchmarks.mpc @@ -0,0 +1,12 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + exename = synch_driver + after += Synch_Lib + libs += Synch_Lib + libpaths += Synch_Lib + source_files { + synch_driver.cpp + } +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.cpp b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.cpp new file mode 100644 index 00000000000..48c2ec20cd0 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.cpp @@ -0,0 +1,95 @@ +// $Id$ + +#if !defined (SYNCHLIB_BUILD_DLL) +#define SYNCHLIB_BUILD_DLL +#endif /* SYNCHLIB_BUILD_DLL */ + +#include "ace/Log_Msg.h" +#include "Benchmark_Base.h" + +ACE_RCSID(Synch_Benchmarks, Benchmark_Base, "$Id$") + +#if defined (ACE_HAS_THREADS) + +// Initialize the static variables. +/* static */ + +Benchmark_Base::Benchmark_Base (int type) + : benchmark_type_ (type) +{ +} + +int +Benchmark_Base::benchmark_type (void) +{ + return this->benchmark_type_; +} + +int +Benchmark_Base::thr_id (void) +{ +#if defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_DCETHREADS) || defined (VXWORKS) + // This invokes the thread-specific storage smart pointer. + return this->id_->thr_id (); +#else + return ACE_Thread::self (); +#endif /* ACE_HAS_PTHREADS || ACE_HAS_DCETHREADS || VXWORKS */ +} + +Benchmark_Method_Base::Benchmark_Method_Base (void) + : Benchmark_Base (Benchmark_Base::METHOD) +{ +} + +int +Benchmark_Method_Base::exec (ACE_Service_Repository_Iterator *sri) +{ + sri->advance (); + for (const ACE_Service_Type *sr; + sri->next (sr) != 0; + sri->advance ()) + { + // This would greatly benefit from RTTI typesafe downcasting... + const ACE_Service_Type_Impl *type = sr->type (); + const void *obj = type->object (); + ACE_Service_Object *so = (ACE_Service_Object *) obj; + Benchmark_Base *bp = (Benchmark_Base *) so; + + if (this->valid_test_object (bp)) + { + + ACE_DEBUG ((LM_DEBUG, "\nstarting up %s\n", sr->name ())); + + int notused = this->pre_run_test (bp) == 0 && this->run_test () == 0 && + this->post_run_test () == 0; + notused = notused; + } + else + return 0; + } + return 0; +} + +#if defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_DCETHREADS) || defined (VXWORKS) +/* static */ +MT_INT Thr_ID::thread_id_ (0); + +Thr_ID::Thr_ID (void) + : thr_id_ (++Thr_ID::thread_id_) +{ +} + +int +Thr_ID::thr_id (void) +{ + return this->thr_id_; +} + +void +Thr_ID::thr_id (int i) +{ + this->thr_id_ = i; +} + +#endif /* ACE_HAS_PTHREADS || ACE_HAS_DCETHREADS || VXWORKS */ +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.h b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.h new file mode 100644 index 00000000000..1737d45b36f --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Benchmark_Base.h @@ -0,0 +1,114 @@ +/* -*- C++ -*- */ +// $Id$ + +/* Defines the base class used to dynamically link in the benchmark tests */ + +#ifndef ACE_BENCHMARK_BASE_H +# define ACE_BENCHMARK_BASE_H + +# include "ace/Service_Config.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +# include "ace/Service_Repository.h" +# include "ace/Service_Types.h" +# include "ace/Atomic_Op.h" +# include "export_mac.h" +# include "ace/TSS_T.h" + +# if defined (ACE_HAS_THREADS) + +# if defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_DCETHREADS) || defined (VXWORKS) + +typedef ACE_Atomic_Op<ACE_Thread_Mutex, int> MT_INT; + +class Thr_ID + // TITLE + // A simple class that provides a thread-specific value in order + // to compensate for POSIX Pthreads. + // + // DESCRIPTION + // Pthreads are too lame to have a sensible scalar values for the + // thread id (unlike Solaris threads). Therefore, we have to + // emulate this ourselves with this class (gag). +{ +public: + Thr_ID (void); + int thr_id (void); + void thr_id (int); + +private: + int thr_id_; + static MT_INT thread_id_; +}; +# endif /* ACE_HAS_PTHREADS || ACE_HAS_DCETHREADS || VXWORKS */ + +class SYNCHLIB_Export Benchmark_Base : public ACE_Service_Object +{ + // = TITLE + // Base class for all benchmarking objects. + // + // = DESCRIPTION + // This class is the base class for all benchmarking + // classes. Its major functionalities are to privide RTTI + // information and to define other common methods all + // benchmarking classes should support. +public: + enum { + BENCHMARK_BASE, + METHOD, + BASELINE, + PERFORMANCE + }; + + int benchmark_type (void); + // RTTI information of this module. + + int thr_id (void); + // Returns our thread id; + +protected: + Benchmark_Base (int type = BENCHMARK_BASE); + // Default ctor. + + int benchmark_type_; + // Store the RTTI info of this module. + +# if defined (ACE_HAS_PTHREADS) || defined (ACE_HAS_DCETHREADS) || defined (VXWORKS) + ACE_TSS <Thr_ID> id_; + // Keeps track of our "virtual" thread id... +# endif /* ACE_HAS_PTHREADS || ACE_HAS_DCETHREADS || VXWORKS */ +}; + +class SYNCHLIB_Export Benchmark_Method_Base : public Benchmark_Base +{ + // = TITLE + // This class identifies itself as Benmarking Method class. + // It defines a method as of how the test is setup and measured. +public: + int exec (ACE_Service_Repository_Iterator *sri); + // Run the test and advanced the service repository iterator + + virtual int pre_run_test (Benchmark_Base *bp) = 0; + // Before running the real test. Subclasses implement this method + // to dictate how the test is performed. + + virtual int run_test (void) = 0; + // Run the real test. Subclasses implement this method to + // dictate how the test is performed. + + virtual int post_run_test (void) = 0; + // After running the real test. Subclasses implement this method to + // dictate how the test is performed. + + virtual int valid_test_object (Benchmark_Base *) = 0; + // Check if we got a valid test to perform. + +protected: + Benchmark_Method_Base (void); +}; + +# endif /* ACE_HAS_THREADS */ +#endif /* ACE_BENCHMARK_BASE_H */ diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Makefile.am b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Makefile.am new file mode 100644 index 00000000000..673ec4a0155 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Makefile.am @@ -0,0 +1,36 @@ +## 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) + +## Makefile.Synch_Lib.am + +noinst_LTLIBRARIES = libSynch_Lib.la + +libSynch_Lib_la_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) \ + -DSYNCHLIB_BUILD_DLL + +libSynch_Lib_la_SOURCES = \ + Benchmark_Base.cpp + +noinst_HEADERS = \ + Benchmark_Base.h \ + export_mac.h + +## 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/Synch-Benchmarks/Synch_Lib/README b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/README new file mode 100644 index 00000000000..f17fe39234a --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/README @@ -0,0 +1,4 @@ +This subdirectory contains a library that defines the interface used +by all benchmarking modules. The library is required by all modules +and the synch_driver. If you want to develop your own benchmarking +module, this directory provides a starting point. diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Synch_Benchmarks_Synch_Lib.mpc b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Synch_Benchmarks_Synch_Lib.mpc new file mode 100644 index 00000000000..34f382f3faa --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/Synch_Benchmarks_Synch_Lib.mpc @@ -0,0 +1,7 @@ +// -*- MPC -*- +// $Id$ + +project(Synch_Lib) : acelib { + sharedname = Synch_Lib + dynamicflags += SYNCHLIB_BUILD_DLL +} diff --git a/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/export_mac.h b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/export_mac.h new file mode 100644 index 00000000000..e4e6be63334 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/Synch_Lib/export_mac.h @@ -0,0 +1,40 @@ +// -*- C++ -*- +// $Id$ +// Definition for Win32 Export directives. +// This file is generated automatically by +// ${ACE_ROOT}/GenExportH.BAT +// ------------------------------ +#if !defined (SYNCHLIB_EXPORT_H) +#define SYNCHLIB_EXPORT_H + +#include "ace/config-all.h" + +#if defined (ACE_AS_STATIC_LIBS) && !defined (SYNCHLIB_HAS_DLL) +# define SYNCHLIB_HAS_DLL 0 +#endif /* ACE_AS_STATIC_LIBS && ! TEST_HAS_DLL */ + +#if !defined (SYNCHLIB_HAS_DLL) +#define SYNCHLIB_HAS_DLL 1 +#endif /* !SYNCHLIB_HAS_DLL */ + +#if defined (SYNCHLIB_HAS_DLL) +# if (SYNCHLIB_HAS_DLL == 1) +# if defined (SYNCHLIB_BUILD_DLL) +# define SYNCHLIB_Export ACE_Proper_Export_Flag +# define SYNCHLIB_SINGLETON_DECLARATION(T) ACE_EXPORT_SINGLETON_DECLARATION (T) +# else +# define SYNCHLIB_Export ACE_Proper_Import_Flag +# define SYNCHLIB_SINGLETON_DECLARATION(T) ACE_IMPORT_SINGLETON_DECLARATION (T) +# endif /* SYNCHLIB_BUILD_DLL */ +# else +# define SYNCHLIB_Export +# define SYNCHLIB_SINGLETON_DECLARATION(T) +# endif /* ! SYNCHLIB_HAS_DLL == 1 */ +#else +# define SYNCHLIB_Export +# define SYNCHLIB_SINGLETON_DECLARATION(T) +#endif /* SYNCHLIB_HAS_DLL */ + +#endif /* SYNCHLIB_EXPORT_H */ +// End of auto generated file. + diff --git a/ACE/performance-tests/Synch-Benchmarks/benchmarks b/ACE/performance-tests/Synch-Benchmarks/benchmarks new file mode 100644 index 00000000000..5b3a6644bc5 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/benchmarks @@ -0,0 +1,19 @@ +#!/bin/csh -f + +echo "Memory test = 512, 40M" +./memory_test -i 80000 -M 512 +echo "Memory test = 1024, 40M" +./memory_test -i 40000 -M 1024 +echo "Memory test = 2048, 40M" +./memory_test -i 20000 -M 2048 +echo "Memory test = 4096, 40M" +./memory_test -i 10000 -M 4096 + +echo "Pipe test = 512, 40M" +./pipe_test -i 80000 -M 512 +echo "Pipe test = 1024, 40M" +./pipe_test -i 40000 -M 1024 +echo "Pipe test = 2048, 40M" +./pipe_test -i 20000 -M 2048 +echo "Pipe test = 4096, 40M" +./pipe_test -i 10000 -M 4096 diff --git a/ACE/performance-tests/Synch-Benchmarks/context.c b/ACE/performance-tests/Synch-Benchmarks/context.c new file mode 100644 index 00000000000..74ace9522aa --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/context.c @@ -0,0 +1,76 @@ +/* $Id$ */ + +/* FUZZ: disable check_for_improper_main_declaration */ + +#include <stdio.h> +// @(#)context.c 1.1 10/18/96 + +#include <stdlib.h> +#include <thread.h> + +#define NSLEEP 100 +#define TMAX 2 +int count[TMAX]; + +void * +work (void *n) +{ + int ni = (int) n; + + while (1) + { + thr_yield (); + count[ni]++; + } + return 0; +} + +main (int argc, char *argv[]) +{ + int ncorr, t1arg, t0arg, orig_ncorr; + thread_t tid1, tid0; + float rate; + + if (argc != 6) + { + printf ("usage: %s t0_bound t0_new_lwp t1_bound t1_new_lwp ncorr\n", argv[0]); + exit (1); + } + t0arg = THR_DETACHED; + if (atoi (argv[1])) + t0arg |= THR_BOUND; + if (atoi (argv[2])) + t0arg |= THR_NEW_LWP; + + t1arg = THR_DETACHED; + if (atoi (argv[3])) + t1arg |= THR_BOUND; + if (atoi (argv[4])) + t1arg |= THR_NEW_LWP; + + ncorr = atoi (argv[5]); + + if (thr_create (0, 0, work, 0, t0arg, &tid0) != 0) + perror ("couldn't create thread 0"); + if (thr_create (0, 0, work, (void *) 1, t1arg, &tid1) != 0) + perror ("couldn't create thread 1"); + + orig_ncorr = thr_getconcurrency (); + if (ncorr) + thr_setconcurrency (ncorr); + sleep (NSLEEP); + rate = (count[0] + count[1]) / ((float) NSLEEP); + printf ("\n------------------------------------------------------------------------\n"); + printf ("t0arg 0x%x (%s, %s, %s)\nt1arg 0x%x (%s, %s, %s)\ncount[0] %d count[1] %d\n\ + ncorr_orig %d ncorr_set %d ncorr_end %d rate %.3f per_cxt %.2f usec\n", + t0arg, + (t0arg & THR_DETACHED) ? "THR_DETACHED" : "Not Detached", + (t0arg & THR_BOUND) ? "THR_BOUND" : "Not Bound", + (t0arg & THR_NEW_LWP) ? "THR_NEW_LWP" : "No New_LWP", + t1arg, + (t1arg & THR_DETACHED) ? "THR_DETACHED" : "Not Detached", + (t1arg & THR_BOUND) ? "THR_BOUND" : "Not Bound", + (t1arg & THR_NEW_LWP) ? "THR_NEW_LWP" : "No New_LWP", + count[0], count[1], + orig_ncorr, ncorr, thr_getconcurrency (), rate, 1.0e6 / rate); +} diff --git a/ACE/performance-tests/Synch-Benchmarks/context.csh b/ACE/performance-tests/Synch-Benchmarks/context.csh new file mode 100644 index 00000000000..867611f07e1 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/context.csh @@ -0,0 +1,16 @@ +#/bin/csh -f +time ./context 0 0 0 0 0 +time ./context 0 0 0 0 2 +time ./context 0 0 0 0 3 + +time ./context 1 0 1 0 0 +time ./context 1 0 1 0 2 +time ./context 1 0 1 0 3 + +time ./context 0 1 0 1 0 +time ./context 0 1 0 1 2 +time ./context 0 1 0 1 3 + +time ./context 1 1 1 1 0 +time ./context 1 1 1 1 2 +time ./context 1 1 1 1 3 diff --git a/ACE/performance-tests/Synch-Benchmarks/orig-results b/ACE/performance-tests/Synch-Benchmarks/orig-results new file mode 100644 index 00000000000..9d4389005f1 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/orig-results @@ -0,0 +1,73 @@ +/* + --------------------- results ------------------------------------- + t0arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + t1arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + count[0] 2222061 count[1] 2222061 + ncorr_orig 1 ncorr_set 0 ncorr_end 2 rate 22070.520 per_cxt 45.31 usec + + ------------------------------------------------------------------------ + t0arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + t1arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + count[0] 3979311 count[1] 3824273 + ncorr_orig 1 ncorr_set 2 ncorr_end 2 rate 38975.535 per_cxt 25.66 usec + + ------------------------------------------------------------------------ + t0arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + t1arg 0x40 (THR_DETACHED, Not Bound, No New_LWP) + count[0] 4173290 count[1] 3690153 + ncorr_orig 1 ncorr_set 3 ncorr_end 3 rate 39134.219 per_cxt 25.55 usec + + ------------------------------------------------------------------------ + t0arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + t1arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + count[0] 1376594 count[1] 1404050 + ncorr_orig 1 ncorr_set 0 ncorr_end 1 rate 13902.920 per_cxt 71.93 usec + + ------------------------------------------------------------------------ + t0arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + t1arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + count[0] 1522495 count[1] 1550889 + ncorr_orig 1 ncorr_set 2 ncorr_end 2 rate 15366.580 per_cxt 65.08 usec + + ------------------------------------------------------------------------ + t0arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + t1arg 0x41 (THR_DETACHED, THR_BOUND, No New_LWP) + count[0] 1282030 count[1] 1265453 + ncorr_orig 1 ncorr_set 3 ncorr_end 3 rate 12737.125 per_cxt 78.51 usec + + ------------------------------------------------------------------------ + t0arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + t1arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + count[0] 3892994 count[1] 3981143 + ncorr_orig 3 ncorr_set 0 ncorr_end 3 rate 39273.352 per_cxt 25.46 usec + + ------------------------------------------------------------------------ + t0arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + t1arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + count[0] 4008638 count[1] 3882986 + ncorr_orig 3 ncorr_set 2 ncorr_end 2 rate 39415.660 per_cxt 25.37 usec + + ------------------------------------------------------------------------ + t0arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + t1arg 0x42 (THR_DETACHED, Not Bound, THR_NEW_LWP) + count[0] 3859767 count[1] 3998157 + ncorr_orig 3 ncorr_set 3 ncorr_end 3 rate 39145.160 per_cxt 25.55 usec + + ------------------------------------------------------------------------ + t0arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + t1arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + count[0] 1557142 count[1] 1588775 + ncorr_orig 3 ncorr_set 0 ncorr_end 3 rate 15729.235 per_cxt 63.58 usec + + ------------------------------------------------------------------------ + t0arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + t1arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + count[0] 1570636 count[1] 1579111 + ncorr_orig 3 ncorr_set 2 ncorr_end 3 rate 15748.395 per_cxt 63.50 usec + + ------------------------------------------------------------------------ + t0arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + t1arg 0x43 (THR_DETACHED, THR_BOUND, THR_NEW_LWP) + count[0] 1414198 count[1] 1371431 + ncorr_orig 3 ncorr_set 3 ncorr_end 3 rate 13927.800 per_cxt 71.80 usec + */ diff --git a/ACE/performance-tests/Synch-Benchmarks/results/.no_prune b/ACE/performance-tests/Synch-Benchmarks/results/.no_prune new file mode 100644 index 00000000000..b5bcdc481b9 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/results/.no_prune @@ -0,0 +1 @@ +"Do not prune me." diff --git a/ACE/performance-tests/Synch-Benchmarks/run_tests.pl b/ACE/performance-tests/Synch-Benchmarks/run_tests.pl new file mode 100755 index 00000000000..92bc3b787fd --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/run_tests.pl @@ -0,0 +1,131 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ + +$EXE = "synch_driver"; +$Win32 = 0; +if ($^O eq "MSWin32") +{ + $Win32 = 1; +} + +$debug = 0; +$name = "release"; +$result_dir = "results"; +$svcconf_dir = "svcconf"; +$conf_ext = ".conf"; + +@Null_List = (); + +# This is called "baseline" +@Baseline_List = ("base_acquire", + "base_tryacquire", + "base_acquire_read", + "base_tryacquire_read", + "base_acquire_write", + "base_tryacquire_write"); + +# this is called "perf_thrno" +@Perf_Thr_Number_List = ("perf_t1", + "perf_t2", + "perf_t4", + "perf_t8", + "perf_t16", + "perf_t32", + "perf_t64"); + +@Target = @Null_List; + +while ( $#ARGV >= 0 && $ARGV[0] =~ /^-/ ) +{ + if ($ARGV[0] eq '-d') # Run debug mode + { + $name = "debug"; + } + elsif ($ARGV[0] eq '-p') # Debug perl script + { + $debug = 1; + print "debug perl scirpt\n"; + } + elsif ($ARGV[0] eq '-D') # Subdir name to put the result + { + shift; + $result_dir = $ARGV[0]; + } + elsif ($ARGV[0] eq '-S') # Subdir to svc.conf files. + { + shift; + $svcconf_dir = $ARGV[0]; + } + elsif ($ARGV[0] eq '-N') # Specify test name. + { + shift; + if ($ARGV[0] eq "baseline") + { + @Target = @Baseline_List; + } + elsif ($ARGV[0] eq "perf_thrno") + { + @Target = @Perf_Thr_Number_List; + } + else + { + die "Unknown test \"$ARGV[0]\"\n"; + } + } + else + { + warn "$0: unknown option $ARGV[0]\n"; + die $usage; + } + shift; +} + +die "You must specify a test to run\n" if (scalar (@Target) == 0); + +if ($Win32 != 0) +{ + $execname = "$name\\$EXE"; + $DIR_SEPARATOR = '\\'; +} +else +{ + $execname = "./$EXE"; # Notice that on UNIX, you much build + # Debug/Release program explicitly + # before running the script. + $DIR_SEPARATOR = '/'; +} + +for ($Cntr = 0; $Cntr < scalar (@Target); $Cntr++) +{ + $Redirect_Output = "$result_dir$DIR_SEPARATOR$Target[$Cntr].$name"; + if ($debug != 0) # Only redirect output in actual run + { + print "Redirecting output to $Redirect_Output\n"; + } + else + { + open STDOUT, "> $Redirect_Output"; + open STDERR, ">&STDOUT"; + } + + @args = ("$execname", + "-f", + "$svcconf_dir$DIR_SEPARATOR$Target[$Cntr]$conf_ext"); + + if ($debug != 0) + { + print "Debug mode: Executing -- "; + for ($args_c = 0; $args_c < scalar (@args); $args_c ++) + { + print "$args[$args_c] "; + } + print "\n"; + } + else + { + system (@args); + } +} diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire.conf new file mode 100644 index 00000000000..2727bac4e73 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline acquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_read.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_read.conf new file mode 100644 index 00000000000..9e25160b8d8 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_read.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline acquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000 -r" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000 -r" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_write.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_write.conf new file mode 100644 index 00000000000..147e04574ca --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_acquire_write.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline acquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000 -w" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000 -w" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire.conf new file mode 100644 index 00000000000..91f78275ffa --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline tryacquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "-t -v" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_read.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_read.conf new file mode 100644 index 00000000000..d300b3b462f --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_read.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline tryacquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "-t -v" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000 -r" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000 -r" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000 -r" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000 -r" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_write.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_write.conf new file mode 100644 index 00000000000..d220d4ecbd5 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/base_tryacquire_write.conf @@ -0,0 +1,20 @@ +# $Id$ +# Benchmark baseline tryacquire operation. + +dynamic Baseline_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Test() "-t -v" +dynamic Baseline_Base_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Base_Test() "-i 10000000 -w" +dynamic Baseline_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Null_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Null_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Thread_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Thread_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_RW_Process_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_RW_Process_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_RW_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_RW_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Recursive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Adaptive_Recursive_Mutex_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Adaptive_Recursive_Mutex_Test() "-i 10000000 -w" +dynamic Baseline_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Process_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Process_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Null_Semaphore_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Null_Semaphore_Test() "-i 10000000 -w" +dynamic Baseline_Token_Test Service_Object * Base_Test/Base_Test:_make_Baseline_Token_Test() "-i 10000000 -w" diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t1.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t1.conf new file mode 100644 index 00000000000..1c4c2ad2f01 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t1.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 1" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t16.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t16.conf new file mode 100644 index 00000000000..f50ae6f4544 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t16.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 16" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t2.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t2.conf new file mode 100644 index 00000000000..cc08f483a73 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t2.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 2" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t32.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t32.conf new file mode 100644 index 00000000000..c12d3dc752b --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t32.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 32" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t4.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t4.conf new file mode 100644 index 00000000000..2d6661d36e1 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t4.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 4" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t64.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t64.conf new file mode 100644 index 00000000000..0bf358d0191 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t64.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 64" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t8.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t8.conf new file mode 100644 index 00000000000..cbfe7062fa8 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/perf_t8.conf @@ -0,0 +1,32 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object * + Perf_Test/Perf_Test:_make_Performance_Test() "-s 60 -N -B -t 8" +dynamic Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Mutex_Test() +dynamic Adaptive_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +dynamic Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +dynamic Adaptive_Recursive_Lock_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +dynamic Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Sema_Test() +dynamic Adaptive_Semaphore_Test + Service_Object * + Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +dynamic RWRD_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWRD_Test() +dynamic RWWR_Mutex_Test + Service_Object * + Perf_Test/Perf_Test:_make_RWWR_Test() +dynamic Token_Test + Service_Object * + Perf_Test/Perf_Test:_make_Token_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/svcconf/svc.conf b/ACE/performance-tests/Synch-Benchmarks/svcconf/svc.conf new file mode 100644 index 00000000000..ec2d85fbcbf --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/svcconf/svc.conf @@ -0,0 +1,29 @@ +# Dynamically configure all the tests + +dynamic Performance_Test + Service_Object + * + Perf_Test/Perf_Test:_make_Performance_Test() + "-s 3 -t 4" +dynamic Mutex_Test Service_Object + * + Perf_Test/Perf_Test:_make_Mutex_Test() +#dynamic Guard_Test Service_Object * Perf_Test/Perf_Test:_make_Guard_Test() "-g" +#dynamic SYSVSema_Test Service_Object * Perf_Test/Perf_Test:_make_SYSVSema_Test() +#dynamic Adaptive_Mutex_Test Service_Object * Perf_Test/Perf_Test:_make_Adaptive_Mutex_Test() +#dynamic Recursive_Lock_Test Service_Object * Perf_Test/Perf_Test:_make_Recursive_Lock_Test() +#dynamic Adaptive_Recursive_Lock_Test Service_Object * +# Perf_Test/Perf_Test:_make_Adaptive_Recursive_Lock_Test() +#dynamic Semaphore_Test Service_Object * Perf_Test/Perf_Test:_make_Sema_Test() +#dynamic Adaptive_Semaphore_Test Service_Object * Perf_Test/Perf_Test:_make_Adaptive_Sema_Test() +#dynamic RWRD_Mutex_Test Service_Object * Perf_Test/Perf_Test:_make_RWRD_Test() +#dynamic RWWR_Mutex_Test Service_Object * Perf_Test/Perf_Test:_make_RWWR_Test() +#dynamic Token_Test Service_Object * Perf_Test/Perf_Test:_make_Token_Test() +#dynamic SYSVSema_Test Service_Object * Perf_Test/Perf_Test:_make_SYSVSema_Test() +#dynamic Context_Test Service_Object * Perf_Test/Perf_Test:_make_Context_Test() +# dynamic Memory_Test Service_Object * Perf_Test/Perf_Test:_make_Memory_Test() +#dynamic Pipe_Thr_Test Service_Object * Perf_Test/Perf_Test:_make_Pipe_Thr_Test() +# dynamic Pipe_Proc_Test Service_Object * Perf_Test/Perf_Test:_make_Pipe_Proc_Test() +# The following two tests don't work correctly yet... +#dynamic Condition_Broadcast_Test Service_Object * Perf_Test/Perf_Test:_make_Cond_Brdcast_Test() +#dynamic Condition_Signal_Test Service_Object * Perf_Test/Perf_Test:_make_Cond_Signal_Test() diff --git a/ACE/performance-tests/Synch-Benchmarks/synch_driver.cpp b/ACE/performance-tests/Synch-Benchmarks/synch_driver.cpp new file mode 100644 index 00000000000..4fb02007eb0 --- /dev/null +++ b/ACE/performance-tests/Synch-Benchmarks/synch_driver.cpp @@ -0,0 +1,52 @@ +// $Id$ + +// Driver program that measures the performance of synchronization +// mechanisms provided by ACE and the underlying OS. + +#include "ace/Log_Msg.h" +#include "ace/Service_Config.h" +#include "ace/Service_Repository.h" +#include "Synch_Lib/Benchmark_Base.h" + +ACE_RCSID(Synch_Benchmarks, synch_driver, "$Id$") + +#if defined (ACE_HAS_THREADS) +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_Service_Config::open (argc, argv); + ACE_Service_Repository_Iterator sri (*ACE_Service_Repository::instance ()); + + // Iteratively execute each service loaded in from the svc.conf + // file. + + for (const ACE_Service_Type *sr; + sri.next (sr) != 0; ) + { + // This would greatly benefit from RTTI typesafe downcasting... + const ACE_Service_Type_Impl *type = sr->type (); + const void *obj = type->object (); + ACE_Service_Object *so = (ACE_Service_Object *) obj; + Benchmark_Base *bb = (Benchmark_Base *) so; + + if (bb->benchmark_type () == Benchmark_Base::METHOD) + { + Benchmark_Method_Base *bm = (Benchmark_Method_Base *) bb; + + ACE_DEBUG ((LM_DEBUG, "\n\nExecuting %s\n", sr->name ())); + + bm->exec (&sri); + } + else + sri.advance (); + } + return 0; +} +#else +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "This test requires the platform to have threads\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/performance-tests/TCP/Makefile.am b/ACE/performance-tests/TCP/Makefile.am new file mode 100644 index 00000000000..828ad0bbbb5 --- /dev/null +++ b/ACE/performance-tests/TCP/Makefile.am @@ -0,0 +1,39 @@ +## 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) + + +## Makefile.TCP.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS = tcp_test + +tcp_test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +tcp_test_SOURCES = \ + tcp_test.cpp + +tcp_test_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/TCP/README b/ACE/performance-tests/TCP/README new file mode 100644 index 00000000000..ac486524c78 --- /dev/null +++ b/ACE/performance-tests/TCP/README @@ -0,0 +1,17 @@ +// $Id$ + +udp_test sends TCP messages and records round-trip latency. The client +records the latencies and provides nice summary statistics. The server +simply echos packets back to the client. + +To run: + 1) On server host: + % ./tcp_test -s + + 2) On client host: + % ./tcp_test -i 10000 <server host> + +The -i option specifies the number of samples (packets to send). +Other command line options are available: ./tcp_test -? to +list them. + diff --git a/ACE/performance-tests/TCP/TCP.mpc b/ACE/performance-tests/TCP/TCP.mpc new file mode 100644 index 00000000000..09cb362e007 --- /dev/null +++ b/ACE/performance-tests/TCP/TCP.mpc @@ -0,0 +1,7 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + avoids += ace_for_tao + exename = tcp_test +} diff --git a/ACE/performance-tests/TCP/run_test.pl b/ACE/performance-tests/TCP/run_test.pl new file mode 100755 index 00000000000..34c4f416c39 --- /dev/null +++ b/ACE/performance-tests/TCP/run_test.pl @@ -0,0 +1,34 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- + +use lib '../../bin'; +use PerlACE::Run_Test; + +$SV = new PerlACE::Process ("tcp_test", "-s"); +$CL = new PerlACE::Process ("tcp_test", "-c localhost -i 50000 -b 64"); + +$status = 0; + +$SV->Spawn (); + +sleep 5; + +$client = $CL->SpawnWaitKill (60); + +$server = $SV->WaitKill (5); + +if ($server != 0) { + print "ERROR: server returned $server\n"; + $status = 1; +} + +if ($client != 0) { + print "ERROR: client returned $client\n"; + $status = 1; +} + +exit $status; diff --git a/ACE/performance-tests/TCP/tcp_test.cpp b/ACE/performance-tests/TCP/tcp_test.cpp new file mode 100644 index 00000000000..168928f4406 --- /dev/null +++ b/ACE/performance-tests/TCP/tcp_test.cpp @@ -0,0 +1,698 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance-tests/TCP +// +// = FILENAME +// tcp_test.cpp +// +// = DESCRIPTION +// Measures TCP round-trip performance. +// +// = AUTHORS +// Based on udp_test by Fred Kuhns and David L. Levine +// Modified by Carlos O'Ryan and Nanbor Wang. +// +// ============================================================================ + +#include "ace/Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Connector.h" +#include "ace/INET_Addr.h" +#include "ace/ACE.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Thread_Manager.h" +#include "ace/Sched_Params.h" +#include "ace/Stats.h" +#include "ace/Throughput_Stats.h" +#include "ace/Sample_History.h" +#include "ace/OS_main.h" +#include "ace/OS_NS_arpa_inet.h" +#include "ace/OS_NS_ctype.h" +#include "ace/OS_NS_errno.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" + +// FUZZ: disable check_for_math_include +#include <math.h> + +ACE_RCSID(TCP, tcp_test, "$Id$") + +// Global variables (evil). +static const u_short DEFPORT = 5050; +static const int MAXPKTSZ = 65536; +static const int DEFPKTSZ = 64; +static const int DEFITERATIONS = 1000; +static const int DEFINTERVAL = 0; +static const int DEFAULT_THRNO = 10; + +static char sbuf[MAXPKTSZ]; +static char rbuf[MAXPKTSZ]; + +static int usdelay = DEFINTERVAL; +static int bufsz = DEFPKTSZ; +static int VERBOSE = 0; +static int dump_history = 0; +static int svr_thrno = DEFAULT_THRNO; +static int server = 0; +static int client = 0; +static int nsamples = DEFITERATIONS; +static int so_bufsz = 0; +static u_int use_reactor = 0; +static int usecs = 0; + +enum { + SELECT = 1, + TP, + WFMO +}; + + +static void +usage (void) +{ + ACE_ERROR ((LM_ERROR, + "tcp_test\n" + " [-v] (Verbose)\n" + " [-h] (dump all the samples)\n" + " [-m message size]\n" + " [-i iterations]\n" + " [-I usdelay]\n" + " [-b socket bufsz] \n" + " [-p port]\n" + " [-s]\n" + " [-c]\n" + // " [-x max_sample_allowed]\n" + " [-t number of threads]\n" + " [-a to use the ACE Select reactor]\n" + " [-x to use the ACE TP reactor]\n" + " [-w to use the ACE WFMO reactor]\n" + " targethost \n")); +} + +// **************************************************************** + +class Client : public ACE_Event_Handler +{ +public: + Client (const ACE_INET_Addr &remote_addr); + + virtual ~Client (void); + + // = Override <ACE_Event_Handler> methods. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + //FUZZ: disable check_for_lack_ACE_OS + int send (const char *buf, size_t len); + // Send the <buf> to the server. + //FUZZ: enable check_for_lack_ACE_OS + + int get_response (char *buf, size_t len); + // Wait for the response. + + int run (void); + // Send messages to server and record statistics. + + //FUZZ: disable check_for_lack_ACE_OS + int shutdown (void); + // Send shutdown message to server. + //FUZZ: enable check_for_lack_ACE_OS + +private: + ACE_SOCK_Stream endpoint_; + // To send messages and receive responses. + + ACE_INET_Addr remote_addr_; + // The address to send messages to. + + ACE_UNIMPLEMENTED_FUNC (Client (void)) + ACE_UNIMPLEMENTED_FUNC (Client (const Client &)) + ACE_UNIMPLEMENTED_FUNC (Client &operator= (const Client &)) +}; + +Client::Client (const ACE_INET_Addr &remote_addr) + : remote_addr_ (remote_addr) +{ + ACE_SOCK_Connector connector; + if (connector.connect (this->endpoint_, remote_addr) == -1) + { + ACE_ERROR ((LM_ERROR, "Client - %p\n", + "connect failed")); + } + + if (use_reactor) + { + if (ACE_Reactor::instance ()->register_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "ACE_Reactor::register_handler: Client\n")); + } +} + +Client::~Client (void) +{ +} + +ACE_HANDLE +Client::get_handle (void) const +{ + return this->endpoint_.get_handle (); +} + +int +Client::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + + ssize_t n = this->endpoint_.recv (buf, sizeof buf); + + if (n == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "handle_input")); + else + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) buf of size %d = %*s\n", + n, + n, + buf)); + + return 0; +} + +int +Client::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + this->endpoint_.close (); + return 0; +} + +int +Client::send (const char *buf, size_t len) +{ + return this->endpoint_.send (buf, len); +} + +int +Client::get_response (char *buf, size_t len) +{ + return this->endpoint_.recv (buf, len); +} + +int +Client::run (void) +{ + ACE_OS::memset (sbuf, 0, bufsz); + ACE_OS::memset (rbuf, 0, bufsz); + + for (int j = 0; j != 100; ++j) + { + if (this->send (sbuf, bufsz) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "send"), -1); + + // ssize_t n; + if ((this->get_response (rbuf, bufsz)) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "get_response"), -1); + } + + ACE_Sample_History history (nsamples); + + ACE_hrtime_t test_start = ACE_OS::gethrtime (); + for (int i = 0; i != nsamples; ++i) + { + if (usecs != 0) + { + ACE_Time_Value tv (0, usecs); + ACE_OS::sleep (tv); + } + + ACE_hrtime_t start = ACE_OS::gethrtime (); + if (this->send (sbuf, bufsz) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "send"), -1); + + // ssize_t n; + if ((this->get_response (rbuf, bufsz)) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "get_response"), -1); + + ACE_hrtime_t end = ACE_OS::gethrtime (); + + history.sample (end - start); + + if (VERBOSE && i % 500 == 0) + { + ACE_DEBUG ((LM_DEBUG, + "Send %d / %d events\n", i, nsamples)); + } + } + ACE_hrtime_t test_end = ACE_OS::gethrtime (); + + ACE_UINT32 gsf = ACE_High_Res_Timer::global_scale_factor (); + + if (dump_history) + { + history.dump_samples (ACE_TEXT("HISTORY"), gsf); + } + + ACE_Basic_Stats latency; + history.collect_basic_stats (latency); + latency.dump_results (ACE_TEXT("Client"), gsf); + ACE_Throughput_Stats::dump_throughput (ACE_TEXT("Client"), + gsf, + test_end - test_start, + latency.samples_count ()); + + + return 0; +} + +int +Client::shutdown (void) +{ + const char buf = 'S'; + int n = this->endpoint_.send (&buf, 1u); + + if (use_reactor) + { + if (ACE_Reactor::instance ()->remove_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler: Client\n"), + -1); + } + + return n; +} + +// **************************************************************** + +class Server : public ACE_Event_Handler +{ +public: + Server (const ACE_INET_Addr &addr); + + virtual ~Server (void); + + // = Override <ACE_Event_Handler> methods. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + +private: + ACE_SOCK_Stream endpoint_; + // Receives datagrams. + + ACE_UNIMPLEMENTED_FUNC (Server (void)) + ACE_UNIMPLEMENTED_FUNC (Server (const Server &)) + ACE_UNIMPLEMENTED_FUNC (Server &operator= (const Server &)) +}; + +Server::Server (const ACE_INET_Addr &addr) +{ + ACE_SOCK_Acceptor acceptor; + + if (acceptor.open (addr, 1) == -1) + ACE_DEBUG ((LM_DEBUG, "%p\n", "open")); + + ACE_DEBUG ((LM_DEBUG, "Listening on %s:%d\n", + addr.get_host_name (), + addr.get_port_number ())); + if (acceptor.accept (this->endpoint_) == -1) + ACE_ERROR ((LM_ERROR, "Server::Server %p\n", + "accept failed")); + + if (use_reactor) + { + if (ACE_Reactor::instance ()->register_handler + (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "ACE_Reactor::register_handler: Server\n")); + } + +#if !defined (ACE_LACKS_SOCKET_BUFSIZ) + if (so_bufsz != 0) + { + if (this->endpoint_.set_option (SOL_SOCKET, + SO_SNDBUF, + (void *) &so_bufsz, + sizeof (so_bufsz)) == -1 + && errno != ENOTSUP) + ACE_ERROR ((LM_ERROR, "Server::Server: SO_SNDBUF %p\n", + "set_option failed")); + else if (this->endpoint_.set_option (SOL_SOCKET, + SO_RCVBUF, + (void *) &so_bufsz, + sizeof (so_bufsz)) == -1 + && errno != ENOTSUP) + ACE_ERROR ((LM_ERROR, "Server::Server: SO_RCVBUF %p\n", + "set_option failed")); + } +#endif /* !ACE_LACKS_SOCKET_BUFSIZ */ + if (acceptor.close () == -1) + ACE_ERROR ((LM_ERROR, "Server::Server %p\n", + "close failed")); +} + +Server::~Server (void) +{ + this->endpoint_.close (); +} + +ACE_HANDLE +Server::get_handle (void) const +{ + return this->endpoint_.get_handle (); +} + +int +Server::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + + ssize_t n = this->endpoint_.recv (buf, bufsz); + + if (n == -1) + ACE_DEBUG ((LM_ERROR, + "%p\n", + "handle_input: recv")); + + // Send the message back as the response. + if (this->endpoint_.send (buf, n) == n) + { + if (n == 1 && buf[0] == 'S') + { + if (!use_reactor) + { + // Indicate done by returning 1. + return 1; + } + + if (ACE_Reactor::instance ()->remove_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler: server\n"), + -1); + + ACE_Reactor::end_event_loop (); + } + + return 0; + } + + ACE_DEBUG ((LM_ERROR, + "%p\n", + "handle_input: send")); + return -1; +} + +int +Server::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + this->endpoint_.close (); + + return 0; +} + +static ACE_THR_FUNC_RETURN +thread_pool_worker (void *) +{ + // Server thread function. + + while (!ACE_Reactor::event_loop_done ()) + { + if (ACE_Reactor::instance ()->handle_events () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("(%t) %p\n"), + ACE_TEXT ("Error handling events"))); + } + + return 0; +} + +int +run_server (ACE_INET_Addr &addr) +{ + if (use_reactor) + { + ACE_Reactor *new_reactor = 0; + + switch (use_reactor) + { + case SELECT: + { + ACE_Select_Reactor *sr = new ACE_Select_Reactor (); + new_reactor = new ACE_Reactor (sr, 1); + } + break; + case TP: + { + ACE_TP_Reactor *sr = new ACE_TP_Reactor (); + new_reactor = new ACE_Reactor (sr, 1); + } + break; + case WFMO: +#if defined (ACE_WIN32) + +#else + +#endif /* ACE_WIN32 */ + default: + ACE_ERROR_RETURN ((LM_ERROR, "Invalid reactor type selected\n"), -1); + } + ACE_Reactor::instance (new_reactor, 1); + } + + Server server (addr); + + if (!use_reactor) + { + // Handle input in the current thread. + // This is actually equivalent to thread-per-connection model. + while (server.handle_input (0) != 1) + continue; + } + else + { + switch (use_reactor) + { + case SELECT: + // Run the reactor event loop. + ACE_Reactor::run_event_loop (); + break; + case TP: + ACE_Thread_Manager::instance ()->spawn_n (svr_thrno, + thread_pool_worker); + ACE_Thread_Manager::instance ()->wait (); + break; + case WFMO: + break; + default: + break; // won't happen here. + } + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int c, dstport = DEFPORT; + int priority = + (ACE_Sched_Params::priority_min (ACE_SCHED_FIFO) + + ACE_Sched_Params::priority_max (ACE_SCHED_FIFO)) / 2; + priority = ACE_Sched_Params::next_priority (ACE_SCHED_FIFO, + priority); + // Enable FIFO scheduling, e.g., RT scheduling class on Solaris. + + if (ACE_OS::sched_params (ACE_Sched_Params (ACE_SCHED_FIFO, + priority, + ACE_SCOPE_PROCESS)) != 0) + { + if (ACE_OS::last_error () == EPERM) + { + ACE_DEBUG ((LM_DEBUG, + "server (%P|%t): user is not superuser, " + "test runs in time-shared class\n")); + } + else + ACE_ERROR ((LM_ERROR, + "server (%P|%t): sched_params failed\n")); + } + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("hxwvb:I:p:sci:m:at:")); + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch ((char) c) + { + case 'v': + VERBOSE = 1; + break; + + case 'h': + dump_history = 1; + break; + + case 'm': + bufsz = ACE_OS::atoi (getopt.opt_arg ()); + + if (bufsz <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nMessage size must be greater than 0!\n\n"), + 1); + else if (bufsz > BUFSIZ) + ACE_ERROR_RETURN ((LM_ERROR, + "\nbufsz must be <= %d\n", + BUFSIZ), + 1); + + case 'i': + nsamples = ACE_OS::atoi (getopt.opt_arg ()); + if (nsamples <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nIterations must be greater than 0!\n\n"), + 1); + break; + + case 'a': + use_reactor = SELECT; + break; + + case 'x': + use_reactor = TP; + break; + + case 'w': +#if defined (ACE_WIN32) + use_reactor = WFMO; + break; +#else + ACE_ERROR_RETURN ((LM_ERROR, "WFMO_Reactor is not supported\n"), -1); +#endif /* ACE_WIN32 */ + + case 'b': + so_bufsz = ACE_OS::atoi (getopt.opt_arg ()); + + if (so_bufsz <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nInvalid socket buffer size!\n\n"), + 1); + break; + + case 'I': + usdelay = ACE_OS::atoi (getopt.opt_arg ()); + + if (usdelay < 0) + { + usdelay = 0; + ACE_ERROR_RETURN ((LM_ERROR, + "%s: bad usdelay: %s\n", + argv[0], + getopt.opt_arg ()), + 1); + } + break; + + case 'p': + dstport = ACE_OS::atoi (getopt.opt_arg ()); + if (dstport <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nInvalid port number!\n\n"), + 1); + break; + case 't': + svr_thrno = ACE_OS::atoi (getopt.opt_arg ()); + + if (svr_thrno <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nInvalid server thread number!\n\n"), + 1); + break; + + case 'c': + server = 0; + client = 1; + break; + case 's': + client = 0; + server = 1; + break; + default: + usage (); + return 1; + } + } + + if (getopt.opt_ind () >= argc && client || argc == 1) + { + usage (); + return 1; + } + + ACE_INET_Addr addr (dstport); + + if (server) + return run_server (addr); + + if ((u_int) bufsz < sizeof (ACE_hrtime_t)) + ACE_ERROR_RETURN ((LM_ERROR, + "\nbufsz must be >= %d\n", + sizeof (ACE_hrtime_t)), + 1); + + ACE_INET_Addr remote_addr; + + if (ACE_OS::ace_isdigit(argv[getopt.opt_ind ()][0])) + { + if (remote_addr.set (dstport, + (ACE_UINT32) ACE_OS::inet_addr + (ACE_TEXT_ALWAYS_CHAR(argv[getopt.opt_ind ()]))) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "invalid IP address: %s\n", + argv[getopt.opt_ind ()]), + 1); + } + else + { + if (remote_addr.set (dstport, argv[getopt.opt_ind ()]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "invalid IP address: %s\n", + argv[getopt.opt_ind ()]), + 1); + } + getopt.opt_ind ()++; + + ACE_DEBUG ((LM_DEBUG, "Connecting to %s:%d\n", + remote_addr.get_host_name (), + remote_addr.get_port_number ())); + + Client client (remote_addr); + + ACE_DEBUG ((LM_DEBUG, + "\nSending %d byte packets to %s:%d " + "with so_bufsz = %d\n\n", + bufsz, + addr.get_host_name (), + dstport, + so_bufsz)); + + client.run (); + client.shutdown (); + + return 0; +} diff --git a/ACE/performance-tests/TTCP/ACE-C++/How_to_run_tests b/ACE/performance-tests/TTCP/ACE-C++/How_to_run_tests new file mode 100644 index 00000000000..1346a745504 --- /dev/null +++ b/ACE/performance-tests/TTCP/ACE-C++/How_to_run_tests @@ -0,0 +1,29 @@ +// ACE version +// +// + +// HOSTNAMES: +// for our tests, encip1-tango.cs.wustl.edu was the receiver hostname used for atm transfers +// tango.cs.wustl.edu was the receiver hostname used for ethernet transfers +// substitute the proper receiver hostname for your system. + +// Results: +// These examples show the result files being stored in a /results directory off +// the current path. This can be set to whatever you like. + +// ATM with 64k receiver buffers +Receiver_Host: repeat 100 server -r -s -fm -p 10002 -b 65536 +Sender_Host: run_test 64 encip1-tango.cs.wustl.edu ./results/ace.atm.64 10002 + or: client -fm -s -t -l 1048576 -n 100 -h encip1-tango.cs.wustl.edu -L ./results/ace.atm.64 -p 10002 + +// ATM with 8k receiver buffers +Receiver_Host: repeat 100 wrapper-new-ttcp -r -s -fm -p 10002 +Sender_Host: run_test 64 encip1-tango.cs.wustl.edu ./results/ace.atm.8 10002 + +// ETHERNET with 64k receiver buffers +Receiver_Host: repeat 100 wrapper-new-ttcp -r -s -fm -p 10002 -b 65536 +Sender_Host: run_test 64 tango.cs.wustl.edu ./results/ace.ethernet.64 10002 + +// ETHERNET with 8k receiver buffers +Receiver_Host: repeat 100 wrapper-new-ttcp -r -s -fm -p 10002 +Sender_Host: run_test 64 tango.cs.wustl.edu ./results/ace.ethernet.8 10002 diff --git a/ACE/performance-tests/TTCP/ACE-C++/run_test b/ACE/performance-tests/TTCP/ACE-C++/run_test new file mode 100644 index 00000000000..e07e6ed0c80 --- /dev/null +++ b/ACE/performance-tests/TTCP/ACE-C++/run_test @@ -0,0 +1,35 @@ +# test_run 64 enatm0-kavita.cs.wustl.edu title 10002 +# repeat 100 wrapper-new-ttcp -r -s -fm -p 10002 -b 65536 +#!/bin/csh -f +if ($#argv != 4) then + echo "Usage: sclt <Max msg size> <destination> <TitleOfThisTest> <port>" $4 + exit 1 +endif +# +@ msize=1024 +@ limit= ($argv[1] * 1024) +#echo $limit +#echo $msize +echo "Iteration#" 1 ": wrapper-new-ttcp -fm -s -t -l" $msize "-h" $2 "-L" $3 "-p" $4 +wrapper-new-ttcp -fm -s -t -l $msize -h $2 -x -L $3 -p $4 +sleep 1 +set flag=0 +while ($msize <= $limit) + if ($flag == 0) goto label + echo "Iteration#" 1 ": wrapper-new-ttcp -fm -s -t -l" $msize "-h" $2 "-x -L" $3 "-p" $4 + wrapper-new-ttcp -fm -s -t -l $msize -h $2 -x -L $3 -p $4 + sleep 1 + label: + set flag=1 + foreach i (2 3 4 5) + echo "Iteration#" $i ": wrapper-new-ttcp -fm -s -t -l" $msize "-h" $2 "-L " $3 "-p" $4 + wrapper-new-ttcp -fm -s -t -l $msize -h $2 -L $3 -p $4 + sleep 1 + end + echo "---------------------------" + @ msize = ($msize * 2) +end + +echo " " +echo "Done at:" +date diff --git a/ACE/performance-tests/TTCP/ACE-C++/wrapper-new-ttcp.cpp b/ACE/performance-tests/TTCP/ACE-C++/wrapper-new-ttcp.cpp new file mode 100644 index 00000000000..a00dd8671f2 --- /dev/null +++ b/ACE/performance-tests/TTCP/ACE-C++/wrapper-new-ttcp.cpp @@ -0,0 +1,967 @@ +// $Id$ + +/* + * T T C P . C + * + * Test TCP connection. Makes a connection on port 5001 + * and transfers fabricated buffers or data copied from stdin. + * + * Usable on 4.2, 4.3, and 4.1a systems by defining one of + * BSD42 BSD43 (BSD41a) + * Machines using System V with BSD sockets should define SYSV. + * + * Modified for operation under 4.2BSD, 18 Dec 84 + * T.C. Slattery, USNA + * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85. + * Modified in 1989 at Silicon Graphics, Inc. + * catch SIGPIPE to be able to print stats when receiver has died + * for tcp, don't look for sentinel during reads to allow small transfers + * increased default buffer size to 8K, nbuf to 2K to transfer 16MB + * moved default port to 5001, beyond IPPORT_USERRESERVED + * make sinkmode default because it is more popular, + * -s now means don't sink/source + * count number of read/write system calls to see effects of + * blocking from full socket buffers + * for tcp, -D option turns off buffered writes (sets TCP_NODELAY sockopt) + * buffer alignment options, -A and -O + * print stats in a format that's a bit easier to use with grep & awk + * for SYSV, mimic BSD routines to use most of the existing timing code + * Modified by Steve Miller of the University of Maryland, College Park + * -b sets the socket buffer size (SO_SNDBUF/SO_RCVBUF) + * Modified Sept. 1989 at Silicon Graphics, Inc. + * restored -s sense at request of tcs@brl + * Modified Oct. 1991 at Silicon Graphics, Inc. + * use getopt(3) for option processing, add -f and -T options. + * SGI IRIX 3.3 and 4.0 releases don't need #define SYSV. + * Modified Aug.1993 at University Paderborn, Germany + * some SVR4 changes and time functions changed to itimer() calls + * Modified by Douglas C. Schmidt September 28, 1994 + * added support for testing UNIX domain socket performance + * Modified by Tim Harrison May, 1995 + * added support for ACE wrappers + * Distribution Status - + * Public Domain. Distribution Unlimited. + */ + +/* #define BSD43 */ +/* #define BSD42 */ +/* #define BSD41a */ +// #define SYSV /* required on SGI IRIX releases before 3.3 */ + +#include "ace/Log_Msg.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <malloc.h> +#include <string.h> +#include <stdlib.h> +#include <memory.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> /* struct itimerval */ +#include <limits.h> +#include <sys/un.h> +#include <unistd.h> + +ACE_SOCK_Connector connector_factory; +ACE_SOCK_Acceptor acceptor_factory; +ACE_INET_Addr address; + +#if defined(SYSV) +#define bcopy(b1,b2,n) memcpy(b2,b1,n) +#define bzero(b1,n) memset(b1,0,n) +#include <sys/times.h> +#include <sys/param.h> + +struct rusage + { + struct timeval ru_utime, ru_stime; + }; +#define RUSAGE_SELF 0 + +#else +#include <sys/resource.h> +#endif + +struct sockaddr_in sinme; +struct sockaddr_un sunme; +struct sockaddr_in sinhim; +struct sockaddr_un sunhim; +struct sockaddr_in frominet; +struct sockaddr_un fromunix; + +struct Session_Control_Message +{ + long nbuf_; + // number of buffers that will be sent this round. + long size_; + // size of the buffers that will be sent +} session_control_buf; + +struct Data_Control_Message +{ + long size_; + char data_; +} *message_buf; + +int fromlen; +int domain = PF_INET; /* Default is to use Internet domain sockets. */ +char *domainname; /* Rendezvous address for UNIX domain sockets. */ +int fd; /* fd of network socket */ + +int data_buf_len = 1024 * 1024 * 2; // length of data portion +long total_msg_len; // length of entire message +char *data_buf; // pointer to data portion +int nbuf = 2 * 1024; /* number of buffers to send in sinkmode */ + +int bufoffset = 0; /* align buffer to this */ +int bufalign = 16 * 1024; /* modulo this */ + +int udp = 0; /* 0 = tcp, !0 = udp */ +int options = 0; /* socket options */ +int one = 1; /* for 4.3 BSD style setsockopt() */ +short port = 5001; /* TCP port number */ +char *host; /* ptr to name of host */ +int trans; /* 0=receive, !0=transmit mode */ +int sinkmode = 0; /* 0=normal I/O, !0=sink/source mode */ +int verbose = 0; /* 0=print basic info, 1=print cpu rate, proc + * resource usage. */ +int nodelay = 0; /* set TCP_NODELAY socket option */ +int b_flag = 0; /* use mread() */ +int sockbufsize = 0; /* socket buffer size to use */ +char fmt = 'K'; /* output format: k = kilobits, K = kilobytes, + * m = megabits, M = megabytes, + * g = gigabits, G = gigabytes */ +int touchdata = 0; /* access data after reading */ + +struct hostent *addr; +extern int errno; +extern int optind; +extern char *optarg; + +char Usage[] = "\ +Usage: ttcp -t [-options] host [ < in ]\n\ + ttcp -r [-options > out]\n\ +Common options:\n\ + -l ## length of bufs read from or written to network (default 8192)\n\ + -u use UDP instead of TCP\n\ + -U use UNIX domain sockets instead of Internet domain sockets\n\ + -p ## port number to send to or listen at (default 5001)\n\ + -s -t: source a pattern to network\n\ + -r: sink (discard) all data from network\n\ + -A align the start of buffers to this modulus (default 16384)\n\ + -O start buffers at this offset from the modulus (default 0)\n\ + -v verbose: print more statistics\n\ + -d set SO_DEBUG socket option\n\ + -b ## set socket buffer size (if supported)\n\ + -f X format for rate: k,K = kilo{bit,byte}; m,M = mega; g,G = giga\n\ +Options specific to -t:\n\ + -n## number of source bufs written to network (default 2048)\n\ + -D don't buffer TCP writes (sets TCP_NODELAY socket option)\n\ +Options specific to -r:\n\ + -B for -s, only output full blocks as specified by -l (for TAR)\n\ + -T \"touch\": access each byte as it's read\n\ +"; + +char stats[128]; +unsigned long nbytes; /* bytes on net */ +unsigned long numCalls = 0; /* # of I/O system calls */ +double cput, realt; /* user, real time (seconds) */ + +void err (char *s); +void mes (char *s); +void pattern (register char *cp, register int cnt); +char *outfmt (double b); +void prep_timer (void); +double read_timer (char *str, int len); +static void prusage (register struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b, char *outp); +static void tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1); +static void tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0); +static void psecs (long l, register char *cp); +void delay (int us); +int mread (int fd, register char *bufp, unsigned n); +int Nread (ACE_SOCK_Stream &s, void *buf, int count); +int Nwrite (ACE_SOCK_Stream &s, void *buf, int count); + +#if !defined (__cplusplus) +typedef void (*SIG_TYP)(); +#else +typedef void (*SIG_TYP)(int); +#endif + +#ifdef SVR4 +void +sigpipe (int foo) +#else +void +sigpipe () +#endif +{ +} + +void sigpipe(int foo) +{ + printf("Caught signal %d\n", foo); +} + +char *title = 0; +int new_line = 0; + +int +ACE_TMAIN(int argc, ACE_TCHAR *argv[]) +{ + ACE_SOCK_Stream connection_stream; + int c; + + printf("HZ = %d\n", HZ); + if (argc < 2) + goto usage; + + while ((c = getopt (argc, argv, "drstU:uvBDTb:f:l:n:p:A:O:L:xh:")) != -1) + { + switch (c) + { + case 'h': + host = optarg; + break; + case 'x': + new_line = 1; + break; + case 'L': + title = optarg; + break; + case 'B': + b_flag = 1; + break; + case 't': + trans = 1; + break; + case 'r': + trans = 0; + break; + case 'd': + options |= SO_DEBUG; + break; + case 'D': + #ifdef TCP_NODELAY + nodelay = 1; + #else + fprintf (stderr, + "ttcp: -D option ignored: TCP_NODELAY socket option not supported\n"); + #endif + break; + case 'n': + nbuf = atoi (optarg); + break; + case 'l': + data_buf_len = atoi (optarg); + break; + case 's': + sinkmode = !sinkmode; + break; + case 'p': + port = atoi (optarg); + break; + case 'U': + domain = PF_UNIX; + domainname = optarg; + break; + case 'u': + udp = 1; + break; + case 'v': + verbose = 1; + break; + case 'A': + bufalign = atoi (optarg); + break; + case 'O': + bufoffset = atoi (optarg); + break; + case 'b': + #if defined(SO_SNDBUF) || defined(SO_RCVBUF) + sockbufsize = atoi (optarg); + #else + fprintf (stderr, + "ttcp: -b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported\n"); + #endif + break; + case 'f': + fmt = *optarg; + break; + case 'T': + touchdata = 1; + break; + + default: + goto usage; + } + } + + /* if transmitter, create remote address to transmit to. */ + + if (trans) + { + if (address.set (port, host) == -1) + perror ("address.set"), exit (1); + } + + /* else, receiver create address to listen on */ + else + { + address.set (port); + } + + total_msg_len = sizeof (long) + data_buf_len; + + // allocate the buffer + message_buf = (Data_Control_Message *) malloc (total_msg_len); + if (message_buf == 0) + err ("malloc"); + +// if (bufalign != 0) +// message_buf += (bufalign - ((int) message_buf % bufalign) + bufoffset) % bufalign; + + // let's go ahead and set the control message for every send right now + message_buf->size_ = data_buf_len; + + session_control_buf.nbuf_ = nbuf; + session_control_buf.size_ = data_buf_len; + + // + // print out option values for trans and receiver + // + + if (trans) + { + fprintf (stdout, + "ttcp-t: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + if (sockbufsize) + fprintf (stdout, ", sockbufsize=%d", sockbufsize); + fprintf (stdout, " %s -> %s\n", + domain == PF_INET ? (udp ? "udp" : "tcp") : "unix", + host == 0 ? domainname : host); + } + else // receiver + { + fprintf (stdout, + "ttcp-r: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + if (sockbufsize) + fprintf (stdout, ", sockbufsize=%d", sockbufsize); + fprintf (stdout, " %s\n", domain == PF_INET ? (udp ? "udp" : "tcp") : "unix"); + } + + mes ("socket"); + + // + // connect and accept + // + + if (!udp) + { + signal (SIGPIPE, (SIG_TYP) sigpipe); + + /* the transmitter will set options and connect to receiver */ + if (trans) + { + // turn off weird ack things + if (nodelay) + { + struct protoent *p = getprotobyname ("tcp"); + + if (p && connection_stream.set_option (p->p_proto, + TCP_NODELAY, + (char *)& one, + sizeof (one))) + err ("setsockopt: nodelay"); + mes ("nodelay"); + } + if (connector_factory.connect (connection_stream, address) == -1) + perror ("connection failed"), exit (1); + fprintf (stdout, + "ttcp-t: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + + if (sockbufsize) + { + if (connection_stream.set_option (SOL_SOCKET, + SO_SNDBUF, + (char *) &sockbufsize, + sizeof sockbufsize) == -1) + err ("acceptor_factory.set_option"); + mes ("sndbuf"); + } + } + + /* receiver will listen for connections from the transmitter */ + else + { + if (acceptor_factory.open (address, 1) == -1) + perror ("acceptor open"), exit (1); + + if (sockbufsize) + { + if (connection_stream.set_option (SOL_SOCKET, + SO_RCVBUF, + (char *) &sockbufsize, + sizeof sockbufsize) == -1) + err ("acceptor_factory.set_option"); + mes ("rcvbuf"); + } + + ACE_INET_Addr remote_address; + + if (acceptor_factory.accept (connection_stream, + (ACE_Addr *) &remote_address) == -1) + perror ("acceptor accept"), exit (1); + + // set the window size + + fprintf (stderr, + "ttcp-r: accept from %s\n", + remote_address.get_host_name()); + } + } + + // + // start timer + // + + errno = 0; + if (trans) + { + pattern (& (message_buf->data_), data_buf_len); + prep_timer (); + + ACE_DEBUG ((LM_DEBUG, "Sending session control message" + " nbuf %d, size %d\n", session_control_buf.nbuf_, + session_control_buf.size_)); + if (connection_stream.send_n ((char *) &session_control_buf, + sizeof (Session_Control_Message)) + != sizeof (Session_Control_Message)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p send session control failed\n", + "ttcp"), + -1); + + long ack; + int send_result; + while (nbuf--) + { + send_result = connection_stream.send_n ((char *) message_buf, + total_msg_len); + if (send_result != total_msg_len) + ACE_ERROR_RETURN ((LM_ERROR, + "%p only sent %d of %d bytes on call %d\n", + "ttcp", send_result, total_msg_len, numCalls + 1), + -1); + numCalls++; + nbytes += data_buf_len; + + if (connection_stream.recv_n ((char *) &ack, sizeof ack) != sizeof ack) + ACE_ERROR_RETURN ((LM_ERROR, "%p recv of ack failed\n", "ttcp"), -1); + + if (ack != data_buf_len) + ACE_DEBUG ((LM_DEBUG, "received ack for only %d bytes\n", ack)); + } + printf("Client finished. \n"); + } + else + { + prep_timer (); + + if (connection_stream.recv_n ((char *) &session_control_buf, + sizeof (Session_Control_Message)) + != sizeof (Session_Control_Message)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p recv session control failed\n", + "ttcp"), + -1); + + ACE_DEBUG ((LM_DEBUG, "received session control message" + " nbuf %d, size %d\n", session_control_buf.nbuf_, + session_control_buf.size_)); + + nbuf = session_control_buf.nbuf_; + // ignore session_control_buf.size_ for now + + long cnt; + + while (nbuf--) + { + if (connection_stream.recv_n ((char *) message_buf, sizeof (long)) + != sizeof (long)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p recv data control failed\n", + "ttcp"), + -1); + + cnt = connection_stream.recv_n (& (message_buf->data_), message_buf->size_); + if (cnt != message_buf->size_) + ACE_ERROR_RETURN ((LM_ERROR, "recv data failed\n"), -1); + + numCalls++; + nbytes += cnt; + + if (connection_stream.send_n ((char *) &cnt, sizeof cnt) + != sizeof cnt) + ACE_ERROR_RETURN ((LM_ERROR, + "%p send ack failed\n", + "ttcp"), + -1); + } + printf("Server finished. \n"); + } + + /* if (errno) + err ("IO"); + */ + // + // stop the timer + // + + (void) read_timer (stats, sizeof (stats)); + if (udp && trans) + { + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + } + if (cput <= 0.0) + cput = 0.001; + if (realt <= 0.0) + realt = 0.001; + +#if defined (LM_RESULTS) + if (trans && (title != 0)) + { + double tmp; + FILE *fd; + char filename[BUFSIZ]; + ACE_OS::sprintf (filename, "%s.results", title); + fd = fopen(filename,"a+"); + if (new_line) + fprintf(fd,"\n -l %ldk \t", data_buf_len/1024); + tmp = ((double) nbytes) / realt; + fprintf(fd,"%.2f ", tmp * 8.0 / 1024.0 / 1024.0); + fclose(fd); + } +#endif + + fprintf (stdout, + "ttcp%s: %ld bytes in %.2f real seconds = %s/sec +++\n", + trans ? "-t" : "-r", + nbytes, realt, outfmt (((double) nbytes) / realt)); + if (verbose) + { + fprintf (stdout, + "ttcp%s: %ld bytes in %.2f CPU seconds = %s/cpu sec\n", + trans ? "-t" : "-r", + nbytes, cput, outfmt (((double) nbytes) / cput)); + } + fprintf (stdout, + "ttcp%s: %d I/O calls, msec/call = %.2f, calls/sec = %.2f\n", + trans ? "-t" : "-r", + numCalls, + 1024.0 * realt / ((double) numCalls), + ((double) numCalls) / realt); + fprintf (stdout, "ttcp%s: %s\n", trans ? "-t" : "-r", stats); + if (verbose) + { + fprintf (stdout, + "ttcp%s: buffer address %#x\n", + trans ? "-t" : "-r", + message_buf); + } + exit (0); + +usage: + fprintf (stderr, Usage); + return 1; +} + +void +err (char *s) +{ + fprintf (stderr, "ttcp%s: ", trans ? "-t" : "-r"); + perror (s); + fprintf (stderr, "errno=%d\n", errno); + exit (1); +} + +void +mes (char *s) +{ + fprintf (stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", s); +} + +void +pattern (register char *cp, register int cnt) +{ + register char c; + c = 0; + while (cnt-- > 0) + { + while (!isprint ((c & 0x7F))) + c++; + *cp++ = (c++ & 0x7F); + } +} + +char * +outfmt (double b) +{ + static char obuf[50]; + switch (fmt) + { + case 'G': + sprintf (obuf, "%.2f GB", b / 1024.0 / 1024.0 / 1024.0); + break; + default: + case 'K': + sprintf (obuf, "%.2f KB", b / 1024.0); + break; + case 'M': + sprintf (obuf, "%.2f MB", b / 1024.0 / 1024.0); + break; + case 'g': + sprintf (obuf, "%.2f Gbit", b * 8.0 / 1024.0 / 1024.0 / 1024.0); + break; + case 'k': + sprintf (obuf, "%.2f Kbit", b * 8.0 / 1024.0); + break; + case 'm': + sprintf (obuf, "%.2f Mbit", b * 8.0 / 1024.0 / 1024.0); + break; + } + return obuf; +} + +static struct itimerval itime0; /* Time at which timing started */ +static struct rusage ru0; /* Resource utilization at the start */ + +#if defined(SYSV) +/*ARGSUSED */ +static void +getrusage (int ignored, register struct rusage *ru) +{ + struct tms buf; + + times (&buf); + + /* Assumption: HZ <= 2147 (LONG_MAX/1000000) */ + ru->ru_stime.tv_sec = buf.tms_stime / HZ; + ru->ru_stime.tv_usec = ((buf.tms_stime % HZ) * 1000000) / HZ; + ru->ru_utime.tv_sec = buf.tms_utime / HZ; + ru->ru_utime.tv_usec = ((buf.tms_utime % HZ) * 1000000) / HZ; +} + +#endif /* SYSV */ +/* + * P R E P _ T I M E R + */ +void +prep_timer () +{ + itime0.it_interval.tv_sec = 0; + itime0.it_interval.tv_usec = 0; + // itime0.it_value.tv_sec = LONG_MAX / 22; /* greatest possible value , itimer() count backwards */ + itime0.it_value.tv_sec = 3600; + itime0.it_value.tv_usec = 0; + + + getrusage (RUSAGE_SELF, &ru0); + fprintf(stdout, "\n"); + fprintf(stdout, "beginning user time = %d sec and %d usec\n", ru0.ru_utime.tv_sec, ru0.ru_utime.tv_usec); + fprintf(stdout, "beginning sys time = %d sec and %d usec\n", ru0.ru_stime.tv_sec, ru0.ru_stime.tv_usec); + + /* Init REAL Timer */ + if (setitimer (ITIMER_REAL, &itime0, 0)) + { + perror ("Setting 'itimer' REAL failed"); + return; + } + fprintf(stdout, "Beginning transaction time = %d sec and %d usec\n", itime0.it_value.tv_sec, itime0.it_value.tv_usec); +} + +/* + * R E A D _ T I M E R + * + */ +double +read_timer (char *str, int len) +{ + struct itimerval itimedol; + struct rusage ru1; + struct timeval td; + struct timeval tend, tstart; + char line[132]; + + getrusage (RUSAGE_SELF, &ru1); + fprintf(stdout, "final user time = %d sec and %d usec\n", ru1.ru_utime.tv_sec, ru1.ru_utime.tv_usec); + fprintf(stdout, "final sys time = %d sec and %d usec\n", ru1.ru_stime.tv_sec, ru1.ru_stime.tv_usec); + + if (getitimer (ITIMER_REAL, &itimedol)) + { + perror ("Getting 'itimer' REAL failed"); + return (0.0); + + } + fprintf(stdout, "End transaction time = %d sec and %d usec\n", itimedol.it_value.tv_sec, itimedol.it_value.tv_usec); + prusage (&ru0, &ru1, &itime0.it_value, &itimedol.it_value, line); + (void) strncpy (str, line, len); + + /* Get real time */ + tvsub (&td, &itime0.it_value, &itimedol.it_value); + realt = td.tv_sec + ((double) td.tv_usec) / 1000000; + + /* Get CPU time (user+sys) */ + tvadd (&tend, &ru1.ru_utime, &ru1.ru_stime); + tvadd (&tstart, &ru0.ru_utime, &ru0.ru_stime); + tvsub (&td, &tend, &tstart); + cput = td.tv_sec + ((double) td.tv_usec) / 1000000; + if (cput < 0.00001) + cput = 0.00001; + return (cput); +} + +static void +prusage (register struct rusage *r0, struct rusage *r1, + struct timeval *b, struct timeval *e, char *outp) +{ + struct timeval tdiff; + register time_t t; + register char *cp; + register int i; + int ms; + + t = (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 1000 + + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 100000 + + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 1000 + + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 100000; + ms = -((e->tv_sec - b->tv_sec) * 1000 + (e->tv_usec - b->tv_usec) / 1000);/* in milliseconds */ + +#define END(x) {while(*x) x++;} +#if defined(SYSV) + cp = "%Uuser %Ssys %Ereal %P"; +#else +#if defined(sgi) /* IRIX 3.3 will show 0 for %M,%F,%R,%C */ + cp = "%Uuser %Ssys %Ereal %P %Mmaxrss %F+%Rpf %Ccsw"; +#else + cp = "%Uutime %Sstime %Edtime %P cpu occupancy"; + /* cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw";*/ +#endif +#endif + for (; *cp; cp++) + { + if (*cp != '%') + *outp++ = *cp; + else if (cp[1]) + switch (*++cp) + { + case 'U': + tvsub (&tdiff, &r1->ru_utime, &r0->ru_utime); + /* sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec);*/ + sprintf (outp, "%f", (tdiff.tv_sec + tdiff.tv_usec/1000000.0)); + END (outp); + break; + + case 'S': + tvsub (&tdiff, &r1->ru_stime, &r0->ru_stime); + /* sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec);*/ + sprintf (outp, "%f", (tdiff.tv_sec + tdiff.tv_usec/1000000.0)); + END (outp); + break; + + case 'E': + psecs (ms / 1000, outp); + END (outp); + break; + + case 'P': + sprintf (outp, "%f%%", (t * 100.0 / ((ms ? ms : 1)))); + END (outp); + break; + + #if !defined(SYSV) + case 'W': + i = r1->ru_nswap - r0->ru_nswap; + sprintf (outp, "%d", i); + END (outp); + break; + + case 'X': + sprintf (outp, "%d", t == 0 ? 0 : (r1->ru_ixrss - r0->ru_ixrss) / t); + END (outp); + break; + + case 'D': + sprintf (outp, "%d", t == 0 ? 0 : + (r1->ru_idrss + r1->ru_isrss - (r0->ru_idrss + r0->ru_isrss)) / t); + END (outp); + break; + + case 'K': + sprintf (outp, "%d", t == 0 ? 0 : + ((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) - + (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t); + END (outp); + break; + + case 'M': + sprintf (outp, "%d", r1->ru_maxrss / 2); + END (outp); + break; + + case 'F': + sprintf (outp, "%d", r1->ru_majflt - r0->ru_majflt); + END (outp); + break; + + case 'R': + sprintf (outp, "%d", r1->ru_minflt - r0->ru_minflt); + END (outp); + break; + + case 'I': + sprintf (outp, "%d", r1->ru_inblock - r0->ru_inblock); + END (outp); + break; + + case 'O': + sprintf (outp, "%d", r1->ru_oublock - r0->ru_oublock); + END (outp); + break; + + case 'C': + sprintf (outp, "%d+%d", r1->ru_nvcsw - r0->ru_nvcsw, + r1->ru_nivcsw - r0->ru_nivcsw); + END (outp); + break; + #endif /* !SYSV */ + } + } + *outp = '\0'; +} + +static void +tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1) +{ + + tsum->tv_sec = t0->tv_sec + t1->tv_sec; + tsum->tv_usec = t0->tv_usec + t1->tv_usec; + if (tsum->tv_usec > 1000000) + tsum->tv_sec++, tsum->tv_usec -= 1000000; +} + +static void +tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0) +{ + + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) + tdiff->tv_sec--, tdiff->tv_usec += 1000000; +} + +static void +psecs (long l, register char *cp) +{ + register int i; + + i = l / 3600; + if (i) + { + sprintf (cp, "%d hours", i); + END (cp); + i = l % 3600; + sprintf (cp, "%d minutes ", (i / 60)); + sprintf (cp, "%d seconds ", (i % 60)); + END (cp); + } + else + { + i = l; + sprintf (cp, "%d minutes ", i / 60); + END (cp); + } + i %= 60; + *cp++ = ':'; + sprintf (cp, "%d seconds ", i); +} + +/* + * N R E A D + */ +int +Nread (ACE_SOCK_Stream &s, void *buf, int count) +{ + numCalls++; + return (s.recv (buf, count)); +} + +/* + * N W R I T E + */ +int +Nwrite (ACE_SOCK_Stream &s, void *buf, int count) +{ + numCalls++; + return s.send (buf, count); +} + +void +delay (int us) +{ + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = us; + (void) select (1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv); +} + +/* + * M R E A D + * + * This function performs the function of a read(II) but will + * call read(II) multiple times in order to get the requested + * number of characters. This can be necessary because + * network connections don't deliver data with the same + * grouping as it is written with. Written by Robert S. Miles, BRL. + */ +int +mread (int fd, register char *bufp, unsigned n) +{ + register unsigned count = 0; + register int nread; + + do + { + nread = read (fd, bufp, n - count); + numCalls++; + if (nread < 0) + { + perror ("ttcp_mread"); + return (-1); + } + if (nread == 0) + return ((int) count); + count += (unsigned) nread; + bufp += nread; + } + while (count < n); + + return ((int) count); +} + diff --git a/ACE/performance-tests/TTCP/C/How_to_run_tests b/ACE/performance-tests/TTCP/C/How_to_run_tests new file mode 100644 index 00000000000..c08a515a5af --- /dev/null +++ b/ACE/performance-tests/TTCP/C/How_to_run_tests @@ -0,0 +1,30 @@ +// C version +// +// + +// HOSTNAMES: +// for our tests, enatm0-tango.cs.wustl.edu was the receiver hostname used for atm transfers +// tango.cs.wustl.edu was the receiver hostname used for ethernet transfers +// substitute the proper receiver hostname for your system. + +// Results: +// These examples show the result files being stored in a /results directory off +// the current path. This can be set to whatever you like. + +// ATM with 32k receiver buffers +Receiver_Host: repeat 100 server -r -s -fm -p 10002 -b 65536 +Sender_Host: run_test 64 encip1-tango.cs.wustl.edu ./results/ace.atm.64 10002 + or: client -fm -s -t -l 1048576 -n 100 -h encip1-tango.cs.wustl.edu -L ./results/ace.atm.64 -p 10002 + +// ATM with 8k receiver buffers +Receiver_Host: repeat 100 new-ttcp -r -s -fm -p 10002 +Sender_Host: run_test 64 enatm0-tango.cs.wustl.edu ./results/c.atm.8 10002 + +// ETHERNET with 64k receiver buffers +Receiver_Host: repeat 100 new-ttcp -r -s -fm -p 10002 -b 65536 +Sender_Host: run_test 64 tango.cs.wustl.edu ./results/c.ethernet.64 10002 + +// ETHERNET with 8k receiver buffers +Receiver_Host: repeat 100 new-ttcp -r -s -fm -p 10002 +Sender_Host: run_test 64 tango.cs.wustl.edu ./results/c.ethernet.8 10002 + diff --git a/ACE/performance-tests/TTCP/C/README b/ACE/performance-tests/TTCP/C/README new file mode 100644 index 00000000000..0da8b3d8010 --- /dev/null +++ b/ACE/performance-tests/TTCP/C/README @@ -0,0 +1,35 @@ + + TTCP for c_version and ACE-wrappers + ---------------------------------------------------- + +This is the super-readme file for the "ttcp" code. This directory +contains the C and ACE-wrappers versions of ttcp code. To compile the +code, you have to do the following: + +1. You should have ACE installed in your system and configured + correctly. (see the reference manual for installation and + configuration procedure) + +2. Copy this directory (ttcp) in the same directory structure to your + disk space. + +3. setup the following environment parameters (before compilation stage): + ACE_ROOT, LD_LIBRARY_PATH. + +For example: +ACE_ROOT=/project/adaptive/ACE_wrappers +LD_LIBRARY_PATH=/project/adaptive/ACE_wrappers/build/SunOS5.4/ace + +4. Do make (using GNU make) in this directory level. This will create + the executable ttcp target code for each ttcp version. + +5. Read the README or How_to_run_tests file existing in each directory + to know how to use the executable. + +6. Have fun. + +Ehab S. Al-Shaer +6/30/1995 + + + diff --git a/ACE/performance-tests/TTCP/C/new-ttcp.cpp b/ACE/performance-tests/TTCP/C/new-ttcp.cpp new file mode 100644 index 00000000000..b8db1e5675c --- /dev/null +++ b/ACE/performance-tests/TTCP/C/new-ttcp.cpp @@ -0,0 +1,987 @@ +// $Id$ + +/* + * T T C P . C + * + * Test TCP connection. Makes a connection on port 5001 + * and transfers fabricated buffers or data copied from stdin. + * + * Usable on 4.2, 4.3, and 4.1a systems by defining one of + * BSD42 BSD43 (BSD41a) + * Machines using System V with BSD sockets should define SYSV. + * + * Modified for operation under 4.2BSD, 18 Dec 84 + * T.C. Slattery, USNA + * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85. + * Modified in 1989 at Silicon Graphics, Inc. + * catch SIGPIPE to be able to print stats when receiver has died + * for tcp, don't look for sentinel during reads to allow small transfers + * increased default buffer size to 8K, nbuf to 2K to transfer 16MB + * moved default port to 5001, beyond IPPORT_USERRESERVED + * make sinkmode default because it is more popular, + * -s now means don't sink/source + * count number of read/write system calls to see effects of + * blocking from full socket buffers + * for tcp, -D option turns off buffered writes (sets TCP_NODELAY sockopt) + * buffer alignment options, -A and -O + * print stats in a format that's a bit easier to use with grep & awk + * for SYSV, mimic BSD routines to use most of the existing timing code + * Modified by Steve Miller of the University of Maryland, College Park + * -b sets the socket buffer size (SO_SNDBUF/SO_RCVBUF) + * Modified Sept. 1989 at Silicon Graphics, Inc. + * restored -s sense at request of tcs@brl + * Modified Oct. 1991 at Silicon Graphics, Inc. + * use getopt(3) for option processing, add -f and -T options. + * SGI IRIX 3.3 and 4.0 releases don't need #define SYSV. + * Modified Aug.1993 at University Paderborn, Germany + * some SVR4 changes and time functions changed to itimer() calls + * Modified by Douglas C. Schmidt September 28, 1994 + * added support for testing UNIX domain socket performance + * Modified by Tim Harrison May, 1995 + * added support for ACE wrappers + * Distribution Status - + * Public Domain. Distribution Unlimited. + */ + +/* #define BSD43 */ +/* #define BSD42 */ +/* #define BSD41a */ +#define SYSV /* required on SGI IRIX releases before 3.3 */ + +//FUZZ: disable check_for_lack_ACE_OS +//FUZZ: disable check_for_improper_main_declaration + +#include "ace/Log_Msg.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" + +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <malloc.h> +#include <string.h> +#include <stdlib.h> +#include <memory.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> /* struct itimerval */ +#include <limits.h> +#include <sys/un.h> +#include <unistd.h> + +ACE_SOCK_Connector connector_factory; +ACE_SOCK_Acceptor acceptor_factory; +ACE_INET_Addr address; + +#if defined(SYSV) +#define bcopy(b1,b2,n) memcpy(b2,b1,n) +#define bzero(b1,n) memset(b1,0,n) +#include <sys/times.h> +#include <sys/param.h> +struct rusage +{ + struct timeval ru_utime, ru_stime; +}; +#define RUSAGE_SELF 0 + +#else +#include <sys/resource.h> +#endif + +ACE_RCSID(C, new_ttcp, "$Id$") + +struct sockaddr_in sinme; +struct sockaddr_un sunme; +struct sockaddr_in sinhim; +struct sockaddr_un sunhim; +struct sockaddr_in frominet; +struct sockaddr_un fromunix; + +int send_n (const void *buf, int len); +int recv_n (void *buf, int len); +int connection_descriptor; + +struct Session_Control_Message +{ + long nbuf_; + // number of buffers that will be sent this round. + long size_; + // size of the buffers that will be sent +} session_control_buf; + +struct Data_Control_Message +{ + long size_; + char data_; +} *message_buf; + +int fromlen; +int domain = PF_INET; /* Default is to use Internet domain sockets. */ +char *domainname; /* Rendezvous address for UNIX domain sockets. */ +int fd; /* fd of network socket */ + +int data_buf_len = 1024 * 1024 * 2; // length of data portion +long total_msg_len; // length of entire message +char *data_buf; // pointer to data portion +int nbuf = 2 * 1024; /* number of buffers to send in sinkmode */ + +int bufoffset = 0; /* align buffer to this */ +int bufalign = 16 * 1024; /* modulo this */ + +int udp = 0; /* 0 = tcp, !0 = udp */ +int options = 0; /* socket options */ +int one = 1; /* for 4.3 BSD style setsockopt() */ +short port = 5001; /* TCP port number */ +char *host; /* ptr to name of host */ +int trans; /* 0=receive, !0=transmit mode */ +int sinkmode = 0; /* 0=normal I/O, !0=sink/source mode */ +int verbose = 0; /* 0=print basic info, 1=print cpu rate, proc + * resource usage. */ +int nodelay = 0; /* set TCP_NODELAY socket option */ +int b_flag = 0; /* use mread() */ +int sockbufsize = 0; /* socket buffer size to use */ +char fmt = 'K'; /* output format: k = kilobits, K = kilobytes, + * m = megabits, M = megabytes, + * g = gigabits, G = gigabytes */ +int touchdata = 0; /* access data after reading */ + +struct hostent *addr; +extern int errno; +extern int optind; +extern char *optarg; + +char Usage[] = "\ + Usage: ttcp -t [-options] host [ < in ]\n\ + ttcp -r [-options > out]\n\ + Common options:\n\ + -l ## length of bufs read from or written to network (default 8192)\n\ + -u use UDP instead of TCP\n\ + -U use UNIX domain sockets instead of Internet domain sockets\n\ + -p ## port number to send to or listen at (default 5001)\n\ + -s -t: source a pattern to network\n\ + -r: sink (discard) all data from network\n\ + -A align the start of buffers to this modulus (default 16384)\n\ + -O start buffers at this offset from the modulus (default 0)\n\ + -v verbose: print more statistics\n\ + -d set SO_DEBUG socket option\n\ + -b ## set socket buffer size (if supported)\n\ + -f X format for rate: k,K = kilo{bit,byte}; m,M = mega; g,G = giga\n\ + Options specific to -t:\n\ + -n## number of source bufs written to network (default 2048)\n\ + -D don't buffer TCP writes (sets TCP_NODELAY socket option)\n\ + Options specific to -r:\n\ + -B for -s, only output full blocks as specified by -l (for TAR)\n\ + -T \"touch\": access each byte as it's read\n\ + "; + +char stats[128]; +unsigned long nbytes; /* bytes on net */ +unsigned long numCalls = 0; /* # of I/O system calls */ +double cput, realt; /* user, real time (seconds) */ + +void err (char *s); +void mes (char *s); +void pattern (register char *cp, register int cnt); +char *outfmt (double b); +static void getrusage (int ignored, register struct rusage *ru); +void prep_timer (void); +double read_timer (char *str, int len); +static void prusage (register struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b, char *outp); +static void tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1); +static void tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0); +static void psecs (long l, register char *cp); +void delay (int us); +int mread (int fd, register char *bufp, unsigned n); +int Nread (ACE_SOCK_Stream &s, void *buf, int count); +int Nwrite (ACE_SOCK_Stream &s, void *buf, int count); + +#if !defined (__cplusplus) +typedef void (*SIG_TYP)(); +#endif + +#ifdef SVR4 +void +sigpipe (int foo) +#else +void +sigpipe () +#endif +{ +} + +char *title = 0; +int new_line = 0; + +int +main (int argc, char *argv[]) +{ + ACE_SOCK_Stream connection_stream; + unsigned long addr_tmp; + int c; + + if (argc < 2) + goto usage; + + while ((c = getopt (argc, argv, "drstU:uvBDTb:f:l:n:p:A:O:L:xh:")) != -1) + { + switch (c) + { + case 'h': + host = optarg; + break; + case 'x': + new_line = 1; + break; + case 'L': + title = optarg; + break; + case 'B': + b_flag = 1; + break; + case 't': + trans = 1; + break; + case 'r': + trans = 0; + break; + case 'd': + options |= SO_DEBUG; + break; + case 'D': + #ifdef TCP_NODELAY + nodelay = 1; + #else + fprintf (stderr, + "ttcp: -D option ignored: TCP_NODELAY socket option not supported\n"); + #endif + break; + case 'n': + nbuf = atoi (optarg); + break; + case 'l': + data_buf_len = atoi (optarg); + break; + case 's': + sinkmode = !sinkmode; + break; + case 'p': + port = atoi (optarg); + break; + case 'U': + domain = PF_UNIX; + domainname = optarg; + break; + case 'u': + udp = 1; + break; + case 'v': + verbose = 1; + break; + case 'A': + bufalign = atoi (optarg); + break; + case 'O': + bufoffset = atoi (optarg); + break; + case 'b': + #if defined(SO_SNDBUF) || defined(SO_RCVBUF) + sockbufsize = atoi (optarg); + #else + fprintf (stderr, + "ttcp: -b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported\n"); + #endif + break; + case 'f': + fmt = *optarg; + break; + case 'T': + touchdata = 1; + break; + + default: + goto usage; + } + } + + /* if transmitter, create remote address to transmit to. */ + + if (trans) + { + if (address.set (port, host) == -1) + perror ("address.set"), exit (1); + } + + /* else, receiver create address to listen on */ + else + { + address.set (port); + } + + total_msg_len = sizeof (long) + data_buf_len; + + // allocate the buffer + message_buf = (Data_Control_Message *) malloc (total_msg_len); + if (message_buf == 0) + err ("malloc"); + + // if (bufalign != 0) + // message_buf += (bufalign - ((int) message_buf % bufalign) + bufoffset) % bufalign; + + // let's go ahead and set the control message for every send right now + message_buf->size_ = data_buf_len; + + session_control_buf.nbuf_ = nbuf; + session_control_buf.size_ = data_buf_len; + + // + // print out option values for trans and receiver + // + + if (trans) + { + fprintf (stdout, + "ttcp-t: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + if (sockbufsize) + fprintf (stdout, ", sockbufsize=%d", sockbufsize); + fprintf (stdout, " %s -> %s\n", + domain == PF_INET ? (udp ? "udp" : "tcp") : "unix", + host == 0 ? domainname : host); + } + else // receiver + { + fprintf (stdout, + "ttcp-r: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + if (sockbufsize) + fprintf (stdout, ", sockbufsize=%d", sockbufsize); + fprintf (stdout, " %s\n", domain == PF_INET ? (udp ? "udp" : "tcp") : "unix"); + } + + mes ("socket"); + + // + // connect and accept + // + + if (!udp) + { + signal (SIGPIPE, (SIG_TYP) sigpipe); + + /* the transmitter will set options and connect to receiver */ + if (trans) + { + if (connector_factory.connect (connection_stream, address) == -1) + perror ("connection failed"), exit (1); + fprintf (stdout, + "ttcp-t: data_buf_len=%d, nbuf=%d, align=%d/%d, port=%d", + data_buf_len, nbuf, bufalign, bufoffset, port); + + // turn off weird ack things + if (nodelay) + { + struct protoent *p = getprotobyname ("tcp"); + + if (p && connection_stream.set_option (p->p_proto, + TCP_NODELAY, + (char *)& one, + sizeof (one))) + err ("setsockopt: nodelay"); + mes ("nodelay"); + } + if (sockbufsize) + { + if (connection_stream.set_option (SOL_SOCKET, + SO_SNDBUF, + (char *) &sockbufsize, + sizeof sockbufsize) == -1) + err ("acceptor_factory.set_option"); + mes ("sndbuf"); + } + } + + /* receiver will listen for connections from the transmitter */ + else + { + if (acceptor_factory.open (address, 1) == -1) + perror ("acceptor open"), exit (1); + + ACE_INET_Addr remote_address; + + if (acceptor_factory.accept (connection_stream, + (ACE_Addr *) &remote_address) == -1) + perror ("acceptor accept"), exit (1); + + // set the window size + if (sockbufsize) + { + if (connection_stream.set_option (SOL_SOCKET, + SO_RCVBUF, + (char *) &sockbufsize, + sizeof sockbufsize) == -1) + err ("acceptor_factory.set_option"); + mes ("rcvbuf"); + } + + fprintf (stderr, + "ttcp-r: accept from %s\n", + remote_address.get_host_name()); + } + } + + // + // start timer + // + + errno = 0; + + // used in send_n and recv_n + connection_descriptor = connection_stream.get_handle (); + + if (trans) + { + pattern (& (message_buf->data_), data_buf_len); + prep_timer (); + + ACE_DEBUG ((LM_DEBUG, "Sending session control message" + " nbuf %d, size %d\n", session_control_buf.nbuf_, + session_control_buf.size_)); + if (send_n ((char *) &session_control_buf, + sizeof (Session_Control_Message)) + != sizeof (Session_Control_Message)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p send session control failed\n", + "ttcp"), + -1); + + long ack; + int send_result; + while (nbuf--) + { + send_result = send_n ((char *) message_buf, total_msg_len); + if (send_result != total_msg_len) + ACE_ERROR_RETURN ((LM_ERROR, + "%p only sent %d of %d bytes on call %d\n", + "ttcp", send_result, total_msg_len, numCalls + 1), + -1); + numCalls++; + nbytes += data_buf_len; + + if (recv_n ((char *) &ack, sizeof ack) != sizeof ack) + ACE_ERROR_RETURN ((LM_ERROR, + "%p recv of ack failed\n", + "ttcp"), + -1); + + if (ack != data_buf_len) + ACE_DEBUG ((LM_DEBUG, "received ack for only %d bytes\n", ack)); + } + } + else + { + prep_timer (); + + if (recv_n ((char *) &session_control_buf, + sizeof (Session_Control_Message)) + != sizeof (Session_Control_Message)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p recv session control failed\n", + "ttcp"), + -1); + + ACE_DEBUG ((LM_DEBUG, "received session control message" + " nbuf %d, size %d\n", session_control_buf.nbuf_, + session_control_buf.size_)); + + nbuf = session_control_buf.nbuf_; + // ignore session_control_buf.size_ for now + + long cnt; + + while (nbuf--) + { + if (recv_n ((char *) message_buf, sizeof (long)) != sizeof (long)) + ACE_ERROR_RETURN ((LM_ERROR, + "%p recv data control failed\n", + "ttcp"), + -1); + + cnt = recv_n (& (message_buf->data_), message_buf->size_); + if (cnt != message_buf->size_) + ACE_ERROR_RETURN ((LM_ERROR, "recv data failed\n"), -1); + + numCalls++; + nbytes += cnt; + + if (send_n ((char *) &cnt, sizeof cnt) != sizeof cnt) + ACE_ERROR_RETURN ((LM_ERROR, + "%p send ack failed\n", + "ttcp"), + -1); + } + } + + if (errno) + err ("IO"); + + // + // stop the timer + // + + (void) read_timer (stats, sizeof (stats)); + if (udp && trans) + { + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + (void) Nwrite (connection_stream, message_buf, 4); /* rcvr end */ + } + if (cput <= 0.0) + cput = 0.001; + if (realt <= 0.0) + realt = 0.001; + +#if defined (LM_RESULTS) + if (trans && (title != 0)) + { + double tmp; + FILE *fd; + char filename[BUFSIZ]; + ACE_OS::sprintf (filename, "%s.results", title); + fd = fopen(filename, "a+"); + if (new_line) + fprintf(fd, "\n -l %ldk \t", data_buf_len/1024); + tmp = ((double) nbytes) / realt; + fprintf(fd, "%.2f ", tmp * 8.0 / 1024.0 / 1024.0); + fclose(fd); + } +#endif + + fprintf (stdout, + "ttcp%s: %ld bytes in %.2f real seconds = %s/sec +++\n", + trans ? "-t" : "-r", + nbytes, realt, outfmt (((double) nbytes) / realt)); + if (verbose) + { + fprintf (stdout, + "ttcp%s: %ld bytes in %.2f CPU seconds = %s/cpu sec\n", + trans ? "-t" : "-r", + nbytes, cput, outfmt (((double) nbytes) / cput)); + } + fprintf (stdout, + "ttcp%s: %d I/O calls, msec/call = %.2f, calls/sec = %.2f\n", + trans ? "-t" : "-r", + numCalls, + 1024.0 * realt / ((double) numCalls), + ((double) numCalls) / realt); + fprintf (stdout, "ttcp%s: %s\n", trans ? "-t" : "-r", stats); + if (verbose) + { + fprintf (stdout, + "ttcp%s: buffer address %#x\n", + trans ? "-t" : "-r", + message_buf); + } + exit (0); + +usage: + fprintf (stderr, Usage); + return 1; +} + +int +send_n (const void *buf, int len) +{ + size_t bytes_written; + int n; + + for (bytes_written = 0; bytes_written < len; bytes_written += n) + if ((n = write (connection_descriptor, (const char *) buf + bytes_written, + len - bytes_written)) == -1) + return -1; + + return bytes_written; +} + +int +recv_n (void *buf, int len) +{ + size_t bytes_read; + int n; + + for (bytes_read = 0; bytes_read < len; bytes_read += n) + if ((n = read (connection_descriptor, (char *) buf + bytes_read, + len - bytes_read)) == -1) + return -1; + else if (n == 0) + break; + + return bytes_read; +} + +void +err (char *s) +{ + fprintf (stderr, "ttcp%s: ", trans ? "-t" : "-r"); + perror (s); + fprintf (stderr, "errno=%d\n", errno); + exit (1); +} + +void +mes (char *s) +{ + fprintf (stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", s); +} + +void +pattern (register char *cp, register int cnt) +{ + register char c; + c = 0; + while (cnt-- > 0) + { + while (!isprint ((c & 0x7F))) + c++; + *cp++ = (c++ & 0x7F); + } +} + +char * +outfmt (double b) +{ + static char obuf[50]; + switch (fmt) + { + case 'G': + sprintf (obuf, "%.2f GB", b / 1024.0 / 1024.0 / 1024.0); + break; + default: + case 'K': + sprintf (obuf, "%.2f KB", b / 1024.0); + break; + case 'M': + sprintf (obuf, "%.2f MB", b / 1024.0 / 1024.0); + break; + case 'g': + sprintf (obuf, "%.2f Gbit", b * 8.0 / 1024.0 / 1024.0 / 1024.0); + break; + case 'k': + sprintf (obuf, "%.2f Kbit", b * 8.0 / 1024.0); + break; + case 'm': + sprintf (obuf, "%.2f Mbit", b * 8.0 / 1024.0 / 1024.0); + break; + } + return obuf; +} + +static struct itimerval itime0; /* Time at which timing started */ +static struct rusage ru0; /* Resource utilization at the start */ + +#if defined(SYSV) +/*ARGSUSED */ +static void +getrusage (int ignored, register struct rusage *ru) +{ + struct tms buf; + + times (&buf); + + /* Assumption: HZ <= 2147 (LONG_MAX/1000000) */ + ru->ru_stime.tv_sec = buf.tms_stime / HZ; + ru->ru_stime.tv_usec = ((buf.tms_stime % HZ) * 1000000) / HZ; + ru->ru_utime.tv_sec = buf.tms_utime / HZ; + ru->ru_utime.tv_usec = ((buf.tms_utime % HZ) * 1000000) / HZ; +} + +#endif /* SYSV */ +/* + * P R E P _ T I M E R + */ +void +prep_timer () +{ + itime0.it_interval.tv_sec = 0; + itime0.it_interval.tv_usec = 0; + itime0.it_value.tv_sec = LONG_MAX / 22; /* greatest possible value , itimer() count backwards */ + itime0.it_value.tv_usec = 0; + + + getrusage (RUSAGE_SELF, &ru0); + + /* Init REAL Timer */ + if (setitimer (ITIMER_REAL, &itime0, 0)) + { + perror ("Setting 'itimer' REAL failed"); + return; + } + +} + +/* + * R E A D _ T I M E R + * + */ +double +read_timer (char *str, int len) +{ + struct itimerval itimedol; + struct rusage ru1; + struct timeval td; + struct timeval tend, tstart; + char line[132]; + + getrusage (RUSAGE_SELF, &ru1); + + if (getitimer (ITIMER_REAL, &itimedol)) + { + perror ("Getting 'itimer' REAL failed"); + return (0.0); + } + + prusage (&ru0, &ru1, &itime0.it_value, &itimedol.it_value, line); + (void) strncpy (str, line, len); + + /* Get real time */ + tvsub (&td, &itime0.it_value, &itimedol.it_value); + realt = td.tv_sec + ((double) td.tv_usec) / 1000000; + + /* Get CPU time (user+sys) */ + tvadd (&tend, &ru1.ru_utime, &ru1.ru_stime); + tvadd (&tstart, &ru0.ru_utime, &ru0.ru_stime); + tvsub (&td, &tend, &tstart); + cput = td.tv_sec + ((double) td.tv_usec) / 1000000; + if (cput < 0.00001) + cput = 0.00001; + return (cput); +} + +static void +prusage (register struct rusage *r0, struct rusage *r1, + struct timeval *e, struct timeval *b, char *outp) +{ + struct timeval tdiff; + register time_t t; + register char *cp; + register int i; + int ms; + + t = (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 + + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 + + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 + + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000; + ms = (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000; + +#define END(x) {while(*x) x++;} +#if defined(SYSV) + cp = "%Uuser %Ssys %Ereal %P"; +#else +#if defined(sgi) /* IRIX 3.3 will show 0 for %M,%F,%R,%C */ + cp = "%Uuser %Ssys %Ereal %P %Mmaxrss %F+%Rpf %Ccsw"; +#else + cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw"; +#endif +#endif + for (; *cp; cp++) + { + if (*cp != '%') + *outp++ = *cp; + else if (cp[1]) + switch (*++cp) + { + case 'U': + tvsub (&tdiff, &r1->ru_utime, &r0->ru_utime); + sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec / 100000); + END (outp); + break; + + case 'S': + tvsub (&tdiff, &r1->ru_stime, &r0->ru_stime); + sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec / 100000); + END (outp); + break; + + case 'E': + psecs (ms / 100, outp); + END (outp); + break; + + case 'P': + sprintf (outp, "%d%%", (int) (t * 100 / ((ms ? ms : 1)))); + END (outp); + break; + + #if !defined(SYSV) + case 'W': + i = r1->ru_nswap - r0->ru_nswap; + sprintf (outp, "%d", i); + END (outp); + break; + + case 'X': + sprintf (outp, "%d", t == 0 ? 0 : (r1->ru_ixrss - r0->ru_ixrss) / t); + END (outp); + break; + + case 'D': + sprintf (outp, "%d", t == 0 ? 0 : + (r1->ru_idrss + r1->ru_isrss - (r0->ru_idrss + r0->ru_isrss)) / t); + END (outp); + break; + + case 'K': + sprintf (outp, "%d", t == 0 ? 0 : + ((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) - + (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t); + END (outp); + break; + + case 'M': + sprintf (outp, "%d", r1->ru_maxrss / 2); + END (outp); + break; + + case 'F': + sprintf (outp, "%d", r1->ru_majflt - r0->ru_majflt); + END (outp); + break; + + case 'R': + sprintf (outp, "%d", r1->ru_minflt - r0->ru_minflt); + END (outp); + break; + + case 'I': + sprintf (outp, "%d", r1->ru_inblock - r0->ru_inblock); + END (outp); + break; + + case 'O': + sprintf (outp, "%d", r1->ru_oublock - r0->ru_oublock); + END (outp); + break; + case 'C': + sprintf (outp, "%d+%d", r1->ru_nvcsw - r0->ru_nvcsw, + r1->ru_nivcsw - r0->ru_nivcsw); + END (outp); + break; + #endif /* !SYSV */ + } + } + *outp = '\0'; +} + +static void +tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1) +{ + + tsum->tv_sec = t0->tv_sec + t1->tv_sec; + tsum->tv_usec = t0->tv_usec + t1->tv_usec; + if (tsum->tv_usec > 1000000) + tsum->tv_sec++, tsum->tv_usec -= 1000000; +} + +static void +tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0) +{ + + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) + tdiff->tv_sec--, tdiff->tv_usec += 1000000; +} + +static void +psecs (long l, register char *cp) +{ + register int i; + + i = l / 3600; + if (i) + { + sprintf (cp, "%d:", i); + END (cp); + i = l % 3600; + sprintf (cp, "%d%d", (i / 60) / 10, (i / 60) % 10); + END (cp); + } + else + { + i = l; + sprintf (cp, "%d", i / 60); + END (cp); + } + i %= 60; + *cp++ = ':'; + sprintf (cp, "%d%d", i / 10, i % 10); +} + +/* + * N R E A D + */ +int +Nread (ACE_SOCK_Stream &s, void *buf, int count) +{ + numCalls++; + return (s.recv (buf, count)); +} + +/* + * N W R I T E + */ +int +Nwrite (ACE_SOCK_Stream &s, void *buf, int count) +{ + numCalls++; + return s.send (buf, count); +} + +void +delay (int us) +{ + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = us; + (void) select (1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv); +} + +/* + * M R E A D + * + * This function performs the function of a read(II) but will + * call read(II) multiple times in order to get the requested + * number of characters. This can be necessary because + * network connections don't deliver data with the same + * grouping as it is written with. Written by Robert S. Miles, BRL. + */ +int +mread (int fd, register char *bufp, unsigned n) +{ + register unsigned count = 0; + register int nread; + + do + { + nread = read (fd, bufp, n - count); + numCalls++; + if (nread < 0) + { + perror ("ttcp_mread"); + return (-1); + } + if (nread == 0) + return ((int) count); + count += (unsigned) nread; + bufp += nread; + } + while (count < n); + + return ((int) count); +} diff --git a/ACE/performance-tests/TTCP/C/run_test b/ACE/performance-tests/TTCP/C/run_test new file mode 100644 index 00000000000..9a4fa346ad3 --- /dev/null +++ b/ACE/performance-tests/TTCP/C/run_test @@ -0,0 +1,35 @@ +# test_run 64 enatm0-kavita.cs.wustl.edu title 10002 +# repeat 100 new-ttcp -r -s -fm -p 10002 -b 65536 +#!/bin/csh -f +if ($#argv != 4) then + echo "Usage: sclt <Max msg size> <destination> <TitleOfThisTest> <port>" $4 + exit 1 +endif +# +@ msize=1024 +@ limit= ($argv[1] * 1024) +#echo $limit +#echo $msize +echo "Iteration#" 1 ": new-ttcp -fm -s -t -l" $msize "-h" $2 "-x -L" $3 "-p" $4 +new-ttcp -fm -s -t -l $msize -h $2 -x -L $3 -p $4 +sleep 1 +set flag=0 +while ($msize <= $limit) + if ($flag == 0) goto label + echo "Iteration#" 1 ": new-ttcp -fm -s -t -l" $msize "-h" $2 "-x -L" $3 "-p" $4 + new-ttcp -fm -s -t -l $msize -h $2 -x -L $3 -p $4 + sleep 1 + label: + set flag=1 + foreach i (2 3 4 5) + echo "Iteration#" $i ": new-ttcp -fm -s -t -l" $msize "-h" $2 "-L" $3 "-p" $4 + new-ttcp -fm -s -t -l $msize -h $2 -L $3 -p $4 + sleep 1 + end + echo "---------------------------" + @ msize = ($msize * 2) +end + +echo " " +echo "Done at:" +date diff --git a/ACE/performance-tests/UDP/Makefile.am b/ACE/performance-tests/UDP/Makefile.am new file mode 100644 index 00000000000..011333ed50e --- /dev/null +++ b/ACE/performance-tests/UDP/Makefile.am @@ -0,0 +1,39 @@ +## 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) + + +## Makefile.UDP.am + +if !BUILD_ACE_FOR_TAO + +noinst_PROGRAMS = udp_test + +udp_test_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +udp_test_SOURCES = \ + udp_test.cpp + +udp_test_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/UDP/README b/ACE/performance-tests/UDP/README new file mode 100644 index 00000000000..f2eb3308e1e --- /dev/null +++ b/ACE/performance-tests/UDP/README @@ -0,0 +1,17 @@ +// $Id$ + +udp_test sends UDP packets and records round-trip latency. The client +records the latencies and provides nice summary statistics. The server +simply echos packets back to the client. + +To run: + 1) On server host: + % ./udp_test -r + + 2) On client host: + % ./udp_test -t -n 1000 <server host> + +The -n option specifies the number of samples (packets to send). +Other command line options are available: ./udp_test -? to +list them. + diff --git a/ACE/performance-tests/UDP/UDP.mpc b/ACE/performance-tests/UDP/UDP.mpc new file mode 100644 index 00000000000..19928979e7b --- /dev/null +++ b/ACE/performance-tests/UDP/UDP.mpc @@ -0,0 +1,10 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + avoids += ace_for_tao + exename = udp_test + verbatim(gnuace, local) { + LDLIBS += $(MATHLIB) + } +} diff --git a/ACE/performance-tests/UDP/run_test.pl b/ACE/performance-tests/UDP/run_test.pl new file mode 100755 index 00000000000..638b8c18c99 --- /dev/null +++ b/ACE/performance-tests/UDP/run_test.pl @@ -0,0 +1,34 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- + +use lib '../../bin'; +use PerlACE::Run_Test; + +$SV = new PerlACE::Process ("udp_test", "-r"); +$CL = new PerlACE::Process ("udp_test", "-t -n 10000 localhost"); + +$status = 0; + +$SV->Spawn (); + +sleep 5; + +$client = $CL->SpawnWaitKill (60); + +$server = $SV->WaitKill (5); + +if ($server != 0) { + print "ERROR: server returned $server\n"; + $status = 1; +} + +if ($client != 0) { + print "ERROR: client returned $client\n"; + $status = 1; +} + +exit $status; diff --git a/ACE/performance-tests/UDP/udp_test.cpp b/ACE/performance-tests/UDP/udp_test.cpp new file mode 100644 index 00000000000..1e59e06b30a --- /dev/null +++ b/ACE/performance-tests/UDP/udp_test.cpp @@ -0,0 +1,741 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// performance-tests/UDP +// +// = FILENAME +// udp_test.cpp +// +// = DESCRIPTION +// Measures UDP round-trip performance. +// +// = AUTHORS +// Fred Kuhns and David L. Levine +// +// ============================================================================ + +#include "ace/OS_main.h" +#include "ace/Reactor.h" +#include "ace/SOCK_Dgram.h" +#include "ace/INET_Addr.h" +#include "ace/ACE.h" +#include "ace/Get_Opt.h" +#include "ace/High_Res_Timer.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_ctype.h" +#include "ace/OS_NS_arpa_inet.h" +#include "ace/OS_NS_string.h" +#include "ace/os_include/os_netdb.h" +#include "ace/OS_NS_unistd.h" + +// FUZZ: disable check_for_math_include +#include <math.h> + +ACE_RCSID(UDP, udp_test, "$Id$") + +// Global variables (evil). +static const u_short DEFPORT = 5050; +static const int MAXPKTSZ = 65536; +static const int DEFPKTSZ = 64; +static const int DEFITERATIONS = 1000; +static const int DEFINTERVAL = 1000; // 1000 usecs. +static const int DEFWINDOWSZ = 10; // 10 microsecond. +static char SendBuf[MAXPKTSZ]; +static char RxBuf[MAXPKTSZ]; +static ACE_TCHAR **cmd; +static ACE_TCHAR datafile[MAXHOSTNAMELEN]; + +static ACE_UINT32 nsamples = DEFITERATIONS; +static int usdelay = DEFINTERVAL; +static int bufsz = DEFPKTSZ; +static int window = DEFWINDOWSZ; +static int VERBOSE = 0; +static int logfile = 0; +static int server = 0; +static int client = 0; +static u_int use_reactor = 0; +ACE_hrtime_t max_allow = 0; +ACE_hrtime_t total_ltime; +ACE_hrtime_t ltime; + +static void +usage (void) +{ + ACE_ERROR ((LM_ERROR, + "%s\n" + "[-w window_size]\n" + " [-f datafile] (creates datafile.samp and datafile.dist)\n" + " [-v] (Verbose)\n" + " [-b send_bufsz]\n" + " [-n nsamples]\n" + " [-I usdelay]\n" + " [-s so_bufsz] \n" + " [-p port]\n" + " [-t]\n" + " [-r]\n" + " [-x max_sample_allowed]\n" + " [-a to use the ACE reactor]\n" + " targethost \n", + *cmd)); +} + +static ACE_hrtime_t *Samples; +static u_int *Dist; +static ACE_TCHAR sumfile[30]; +static ACE_TCHAR distfile[30]; +static ACE_TCHAR sampfile[30]; + +class Client : public ACE_Event_Handler +{ +public: + Client (const ACE_INET_Addr &addr, + const ACE_INET_Addr &remote_addr); + + virtual ~Client (void); + + // = Override <ACE_Event_Handler> methods. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + //FUZZ: disable check_for_lack_ACE_OS + int send (const char *buf, size_t len); + // Send the <buf> to the server. + //FUZZ: enable check_for_lack_ACE_OS + + int get_response (char *buf, size_t len); + // Wait for the response. + + int run (void); + // Send messages to server and record statistics. + + //FUZZ: disable check_for_lack_ACE_OS + int shutdown (void); + // Send shutdown message to server. + //FUZZ: enable check_for_lack_ACE_OS + +private: + ACE_SOCK_Dgram endpoint_; + // To send messages and receive responses. + + ACE_INET_Addr remote_addr_; + // The address to send messages to. + + ACE_UNIMPLEMENTED_FUNC (Client (void)) + ACE_UNIMPLEMENTED_FUNC (Client (const Client &)) + ACE_UNIMPLEMENTED_FUNC (Client &operator= (const Client &)) +}; + +Client::Client (const ACE_INET_Addr &addr, + const ACE_INET_Addr &remote_addr) + : endpoint_ (addr), + remote_addr_ (remote_addr) +{ + if (use_reactor) + { + if (ACE_Reactor::instance ()->register_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "ACE_Reactor::register_handler: Client\n")); + } +} + +Client::~Client (void) +{ +} + +ACE_HANDLE +Client::get_handle (void) const +{ + return endpoint_.get_handle (); +} + +int +Client::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + ACE_INET_Addr from_addr; + + ssize_t n = endpoint_.recv (buf, sizeof buf, from_addr); + + if (n == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "handle_input")); + else + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) buf of size %d = %*s\n", + n, + n, + buf)); + + return 0; +} + +int +Client::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + this->endpoint_.close (); + return 0; +} + +int +Client::send (const char *buf, size_t len) +{ + return this->endpoint_.send (buf, len, remote_addr_); +} + +int +Client::get_response (char *buf, size_t len) +{ + ACE_INET_Addr addr; + return this->endpoint_.recv (buf, len, addr); +} + +int +Client::run (void) +{ + int ndist = 0; + int i; + int j; + int n; + int maxindx = 0; + int minindx = 0; + char *sbuf = SendBuf; + char *rbuf = RxBuf; + + ACE_High_Res_Timer timer; + ACE_hrtime_t sample; + + int d; + double std_dev = 0.0; + double std_err = 0.0; + double sample_mean = 0.0; + + int tracking_last_over = 0; + ACE_High_Res_Timer since_over; + ACE_hrtime_t psum = 0; + ACE_hrtime_t sum = 0; + ACE_hrtime_t max = 0; + ACE_hrtime_t min = (ACE_hrtime_t) (u_int) -1; + FILE *sumfp = 0; + FILE *distfp = 0; + FILE *sampfp = 0; + pid_t *pid = (pid_t *) sbuf; + int *seq = (int *) (sbuf + sizeof (int)); + + ACE_OS::memset (sbuf, 0, bufsz); + ACE_OS::memset (rbuf, 0, bufsz); + + *pid = ACE_OS::getpid (); + *seq = 0; + + ACE_DEBUG ((LM_DEBUG, + "PID = %d, Starting SEQ = %d\n", + *pid, + *seq)); + + // Allocate memory to hold samples. + Samples = (ACE_hrtime_t *) ACE_OS::calloc (nsamples, + sizeof (ACE_hrtime_t)); + + for (i = -1, *seq = 0, j = 0; + i < (ACE_INT32) nsamples; + (*seq)++, i++, j++, timer.reset ()) + { + timer.start (); + if (this->send (sbuf, bufsz) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "send"), -1); + + if ((n = get_response (rbuf, bufsz)) <= 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%P) %p\n", "get_response"), -1); + + timer.stop (); + + if (n <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nTrouble receiving from socket!\n\n"), + -1); + + timer.elapsed_time (sample); // in nanoseconds. + + if (i < 0 ) + { +#ifndef ACE_LACKS_LONGLONG_T + ACE_DEBUG ((LM_DEBUG, + "Ignoring first sample of %u usecs\n", + (ACE_UINT32) (sample))); +#else + ACE_DEBUG ((LM_DEBUG, + "Ignoring first sample of %u usecs\n", + (ACE_UINT32) (sample.lo()))); +#endif + continue; + } + else if (max_allow > 0 && sample > max_allow) + { + ACE_DEBUG ((LM_DEBUG, "Sample # %i = " + "%u msec is over the limit (%u)!\n", + i, + (ACE_UINT32) (sample / (ACE_UINT32) 1000000), + (ACE_UINT32) (max_allow / (ACE_UINT32) 1000000))); + + if (tracking_last_over) + { + since_over.stop (); + ACE_Time_Value over_time; + since_over.elapsed_time (over_time); + ACE_DEBUG ((LM_DEBUG, + "\tTime since last over = %u msec!\n", + over_time.msec ())); + since_over.reset (); + } + + tracking_last_over = 1; + since_over.start (); + i--; + continue; + } + + Samples[i] = sample; + sum += sample; + + if (min == (ACE_hrtime_t) (u_int) -1) + { + min = sample; + minindx = i; + } + if (sample > max) + { + max = sample; + maxindx = i; + } + if (sample < min) + { + min = sample; + minindx = i; + } + + if (VERBOSE) + { + psum += sample; + if (j == 500) + { + ACE_DEBUG ((LM_DEBUG, + "(%i) Partial (running) mean %u usecs\n", + i + 1, + (ACE_UINT32) (psum / (ACE_UINT32) (1000 * 500)))); + j = 0; + psum = 0; + } + } + } + + sample_mean = ((double) ACE_U64_TO_U32 (sum)) / (double) nsamples; + + if (logfile) + { + ACE_OS::sprintf (sumfile, ACE_TEXT("%s.sum"), datafile); + ACE_OS::sprintf (distfile, ACE_TEXT("%s.dist"), datafile); + ACE_OS::sprintf (sampfile, ACE_TEXT("%s.samp"), datafile); + + distfp = ACE_OS::fopen(distfile, ACE_TEXT("w")); + + if (distfp == 0) + { + ACE_DEBUG ((LM_DEBUG, + "Unable to open dist file!\n\n")); + logfile = 0; + } + if (logfile && (sampfp = ACE_OS::fopen (sampfile, ACE_TEXT("w"))) == 0) + { + ACE_OS::fclose (distfp); + ACE_DEBUG ((LM_DEBUG, + "Unable to open sample file!\n\n")); + logfile = 0; + } + if (logfile && (sumfp = ACE_OS::fopen (sumfile, ACE_TEXT("w"))) == 0) + { + ACE_OS::fclose (distfp); + ACE_OS::fclose (sampfp); + ACE_DEBUG ((LM_DEBUG, + "Unable to open sample file!\n\n")); + logfile = 0; + } + } + + if (window) + { + window = window * 1000; // convert to nsec. + ndist = (int)((max-min) / window) + 1; + Dist = (u_int *) ACE_OS::calloc (ndist, + sizeof (u_int)); + } + + for (i = 0; i < (ACE_INT32) nsamples; i++) + { + std_dev += ((double) ACE_U64_TO_U32 (Samples[i]) - sample_mean) * + ((double) ACE_U64_TO_U32 (Samples[i]) - sample_mean); + d = (int)((Samples[i] - min)/window); + + if (d < 0 || d > ndist) + { + ACE_DEBUG ((LM_DEBUG, + "\nError indexing into dist array %d (%d)\n\n", + d, + ndist)); + ACE_OS::exit (1); + } + Dist[d] += 1; + if (logfile) + ACE_OS::fprintf (sampfp, + "%u\n", + ACE_U64_TO_U32 (Samples[i])); + } + + if (logfile) + { + ACE_hrtime_t tmp; + tmp = min + (window / 2); + + for (i = 0; i < ndist; i++) + { + ACE_OS::fprintf (distfp, + "%u %d\n", + (ACE_UINT32) (tmp / (ACE_UINT32) 1000), + Dist[i]); + tmp += window; + } + } + + std_dev = (double) sqrt (std_dev / (double) (nsamples - 1.0)); + std_err = (double) std_dev / sqrt ((double) nsamples); + + ACE_DEBUG ((LM_DEBUG, + "\nResults for %i samples (usec):\n" + "\tSample Mean = %f,\n" + "\tSample Max = %u, Max index = %d,\n" + "\tSample Min = %u, Min index = %d,\n" + "\tStandard Deviation = %f,\n" + "\tStandard Error = %f\n", + nsamples, + sample_mean / 1000.0, + (u_int) (max / (ACE_UINT32) 1000), + maxindx, + (u_int) (min / (ACE_UINT32) 1000), + minindx, + std_dev / 1000.0, + std_err / 1000.0)); + + if (logfile) + { + ACE_OS::fprintf (sumfp, + "Command executed: \n"); + for (; *cmd; cmd++) + ACE_OS::fprintf (sumfp, + "%s ", + *cmd); + ACE_OS::fprintf (sumfp, + "\n"); + + ACE_OS::fprintf (sumfp, + "\nResults for %i samples (usec):" + "\tSample Mean = %f,\n" + "\tSample Max = %u, Max index = %d,\n" + "\tSample Min = %u, Min index = %d,\n" + "\tStandard Deviation = %f,\n" + "\tStandard Error = %f\n", + nsamples, + sample_mean / 1000.0, + (ACE_UINT32) (max / (ACE_UINT32) 1000), + maxindx, + (ACE_UINT32) (min / (ACE_UINT32) 1000), + minindx, + std_dev / 1000.0, + std_err / 1000.0); + } + + return 0; +} + +int +Client::shutdown (void) +{ + const char buf = 'S'; + const int n = endpoint_.send (&buf, 1u, remote_addr_); + + if (use_reactor) + { + if (ACE_Reactor::instance ()->remove_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler: Client\n"), + -1); + } + + return n; +} + +class Server : public ACE_Event_Handler +{ +public: + Server (const ACE_INET_Addr &addr); + + virtual ~Server (void); + + // = Override <ACE_Event_Handler> methods. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + +private: + ACE_SOCK_Dgram endpoint_; + // Receives datagrams. + + ACE_UNIMPLEMENTED_FUNC (Server (void)) + ACE_UNIMPLEMENTED_FUNC (Server (const Server &)) + ACE_UNIMPLEMENTED_FUNC (Server &operator= (const Server &)) +}; + +Server::Server (const ACE_INET_Addr &addr) + : endpoint_ (addr) +{ + if (use_reactor) + { + if (ACE_Reactor::instance ()->register_handler + (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "ACE_Reactor::register_handler: Server\n")); + } +} + +Server::~Server (void) +{ +} + +ACE_HANDLE +Server::get_handle (void) const +{ + return endpoint_.get_handle (); +} + +int +Server::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + ACE_INET_Addr from_addr; + + ssize_t n = endpoint_.recv (buf, sizeof buf, from_addr); + + if (n == -1) + ACE_DEBUG ((LM_ERROR, + "%p\n", + "handle_input: recv")); + + // Send the message back as the response. + if (endpoint_.send (buf, n, from_addr) == n) + { + if (n == 1 && buf[0] == 'S') + { + if (use_reactor) + { + if (ACE_Reactor::instance ()->remove_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler: server\n"), + -1); + + ACE_Reactor::end_event_loop (); + } + else + { + // Indicate done by returning 1. + return 1; + } + } + + return 0; + } + else + { + ACE_DEBUG ((LM_ERROR, + "%p\n", + "handle_input: send")); + return -1; + } +} + +int +Server::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + endpoint_.close (); + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int c, dstport = DEFPORT; + int so_bufsz = 0; + + cmd = argv; + + //FUZZ: disable check_for_lack_ACE_OS + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("x:w:f:vs:I:p:rtn:b:a")); + + while ((c = getopt ()) != -1) + { + //FUZZ: enable check_for_lack_ACE_OS + switch ((char) c) + { + case 'x': + max_allow = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'w': + window = ACE_OS::atoi (getopt.opt_arg ()); + if (window < 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Invalid window!\n\n"), + 1); + break; + case 'f': + ACE_OS::strcpy (datafile, getopt.opt_arg ()); + logfile = 1; + break; + case 'v': + VERBOSE = 1; + break; + case 'b': + bufsz = ACE_OS::atoi (getopt.opt_arg ()); + + if (bufsz <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nBuffer size must be greater than 0!\n\n"), + 1); + + case 'n': + nsamples = ACE_OS::atoi (getopt.opt_arg ()); + if (nsamples <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nIterations must be greater than 0!\n\n"), + 1); + break; + case 'a': + use_reactor = 1; + break; + case 's': + so_bufsz = ACE_OS::atoi (getopt.opt_arg ()); + + if (so_bufsz <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nInvalid socket buffer size!\n\n"), + 1); + break; + case 'I': + usdelay = ACE_OS::atoi (getopt.opt_arg ()); + + if (usdelay == 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%s: bad usdelay: %s\n", + argv[0], + getopt.opt_arg ()), + 1); + break; + case 'p': + dstport = ACE_OS::atoi (getopt.opt_arg ()); + if (dstport <= 0) + ACE_ERROR_RETURN ((LM_ERROR, + "\nInvalid port number!\n\n"), + 1); + break; + case 't': + server = 0; + client = 1; + break; + case 'r': + client = 0; + server = 1; + break; + default: + usage (); + return 1; + } + } + + if (getopt.opt_ind () >= argc && client || argc == 1) + { + usage (); + return 1; + } + + ACE_INET_Addr addr (server ? dstport : dstport + 1); + + if (server) + { + Server server (addr); + + if (use_reactor) + { + ACE_Reactor::run_event_loop (); + } + else + { + // Handle input in the current thread. + while (server.handle_input (0) != 1) + continue; + } + } + else + { + if ((u_int) bufsz < sizeof (ACE_hrtime_t)) + ACE_ERROR_RETURN ((LM_ERROR, + "\nbufsz must be >= %d\n", + sizeof (ACE_hrtime_t)), + 1); + ACE_INET_Addr remote_addr; + + if (ACE_OS::ace_isdigit(argv[getopt.opt_ind ()][0])) + { + if (remote_addr.set (dstport, + (ACE_UINT32) ACE_OS::inet_addr + (ACE_TEXT_ALWAYS_CHAR(argv[getopt.opt_ind ()]))) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "invalid IP address: %s\n", + argv[getopt.opt_ind ()]), + 1); + } + else + { + if (remote_addr.set (dstport, argv[getopt.opt_ind ()]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "invalid IP address: %s\n", + argv[getopt.opt_ind ()]), + 1); + } + getopt.opt_ind ()++; + + Client client (addr, remote_addr); + + ACE_DEBUG ((LM_DEBUG, + "\nSending %d byte packets to %s:%d " + "with so_bufsz = %d\n\n", + bufsz, + addr.get_host_name (), + dstport, + so_bufsz)); + + client.run (); + client.shutdown (); + } + + return 0; +} diff --git a/ACE/performance-tests/perf.mwc b/ACE/performance-tests/perf.mwc new file mode 100644 index 00000000000..dd297256b34 --- /dev/null +++ b/ACE/performance-tests/perf.mwc @@ -0,0 +1,5 @@ +// -*- MPC -*- +// $Id$ + +workspace { +} |