summaryrefslogtreecommitdiff
path: root/ACE/ace/High_Res_Timer.cpp
diff options
context:
space:
mode:
authorWilliam R. Otte <wotte@dre.vanderbilt.edu>2006-07-24 15:50:30 +0000
committerWilliam R. Otte <wotte@dre.vanderbilt.edu>2006-07-24 15:50:30 +0000
commitc44379cc7d9c7aa113989237ab0f56db12aa5219 (patch)
tree66a84b20d47f2269d8bdc6e0323f338763424d3a /ACE/ace/High_Res_Timer.cpp
parent3aff90f4a822fcf5d902bbfbcc9fa931d6191a8c (diff)
downloadATCD-c44379cc7d9c7aa113989237ab0f56db12aa5219.tar.gz
Repo restructuring
Diffstat (limited to 'ACE/ace/High_Res_Timer.cpp')
-rw-r--r--ACE/ace/High_Res_Timer.cpp546
1 files changed, 546 insertions, 0 deletions
diff --git a/ACE/ace/High_Res_Timer.cpp b/ACE/ace/High_Res_Timer.cpp
new file mode 100644
index 00000000000..7bfd15db279
--- /dev/null
+++ b/ACE/ace/High_Res_Timer.cpp
@@ -0,0 +1,546 @@
+// $Id$
+
+// Be very carefull before changing the calculations inside
+// ACE_High_Res_Timer. The precision matters and we are using integer
+// calculations not floating point. Also look good at the emulated 64
+// bit int class (inside Basic_Types{h,i,cpp} before changing
+// anything. It's operator/ only returns 32 bits not 64 bits, among
+// other things.
+
+#include "ace/High_Res_Timer.h"
+
+#if !defined (__ACE_INLINE__)
+#include "ace/High_Res_Timer.inl"
+#endif /* __ACE_INLINE__ */
+
+#include "ace/Stats.h"
+#include "ace/OS_NS_stdio.h"
+#include "ace/OS_NS_string.h"
+#include "ace/OS_NS_sys_time.h"
+#include "ace/OS_NS_time.h"
+#include "ace/OS_NS_unistd.h"
+#include "ace/OS_NS_stdlib.h"
+
+ACE_RCSID(ace, High_Res_Timer, "$Id$")
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ACE_ALLOC_HOOK_DEFINE(ACE_High_Res_Timer)
+
+ACE_END_VERSIONED_NAMESPACE_DECL
+
+// For Intel platforms, a scale factor is required for
+// ACE_OS::gethrtime. We'll still set this to one to prevent division
+// by zero errors.
+#if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
+ defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
+ !defined (ACE_HAS_HI_RES_TIMER)
+
+# include "ace/Guard_T.h"
+# include "ace/Recursive_Thread_Mutex.h"
+# include "ace/Object_Manager.h"
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ // Initialize the global_scale_factor_ to 1. The first
+ // ACE_High_Res_Timer instance construction will override this
+ // value.
+ /* static */
+ ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1u;
+
+ACE_END_VERSIONED_NAMESPACE_DECL
+
+#else /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
+ ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) ||
+ ACE_HAS_HI_RES_TIMER */
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+ // A scale_factor of 1000 converts nanosecond ticks to microseconds.
+ // That is, on these platforms, 1 tick == 1 nanosecond.
+ /* static */
+ ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1000u;
+
+ACE_END_VERSIONED_NAMESPACE_DECL
+#endif /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
+ ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) ||
+ ACE_HAS_HI_RES_TIMER */
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+// This is used to tell if the global_scale_factor_ has been
+// set, and if high resolution timers are supported.
+/* static */
+int ACE_High_Res_Timer::global_scale_factor_status_ = 0;
+
+
+#if defined (linux)
+// Determine the apparent CPU clock speed from /proc/cpuinfo
+ACE_UINT32
+ACE_High_Res_Timer::get_cpuinfo (void)
+{
+ ACE_UINT32 scale_factor = 1u;
+
+ // Get the BogoMIPS from /proc/cpuinfo. It works fine on Alpha and
+ // Pentium Pro. For other CPUs, it will be necessary to interpret
+ // the BogoMips, as described in the BogoMips mini-HOWTO. Note that
+ // this code assumes an order to the /proc/cpuinfo contents. The
+ // BogoMips rating had better come after CPU type and model info.
+#if !defined (__alpha__)
+ int supported = 0;
+#endif /* __alpha__ */
+
+ FILE *cpuinfo = ACE_OS::fopen (ACE_LIB_TEXT ("/proc/cpuinfo"),
+ ACE_LIB_TEXT ("r"));
+
+ if (cpuinfo != 0)
+ {
+ char buf[128];
+
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nReading /proc/cpuinfo...")));
+
+ while (ACE_OS::fgets (buf, sizeof buf, cpuinfo))
+ {
+#if defined (__alpha__)
+ ACE_UINT32 whole;
+ ACE_UINT32 fractional;
+ if (::sscanf (buf,
+ "BogoMIPS : %d.%d\n",
+ &whole,
+ &fractional) == 2
+ || ::sscanf (buf,
+ "bogomips : %d.%d\n",
+ &whole,
+ &fractional) == 2)
+ {
+ scale_factor = whole;
+ break;
+ }
+#else
+ double mhertz = 1;
+ double bmips = 1;
+ char arg[128];
+
+ // CPU type?
+ if (::sscanf (buf, "cpu : %s\n", arg) == 1)
+ {
+ // If this is an Alpha chip, then the BogoMips rating is
+ // usable...
+ if (ACE_OS::strncmp (arg,
+ "Alpha",
+ 5) == 0)
+ {
+ supported = 1;
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" recognized Alpha chip...")));
+ }
+ }
+ // Pentium CPU model?
+ else if (supported == 0
+ && ::sscanf (buf, "model name : Pentium %s\n", arg) == 1)
+ {
+ // But if we don't have the right kind of Intel chip,
+ // just quit.
+ if (ACE_OS::strcmp (arg, "II") == 0
+ || ACE_OS::strcmp (arg, "III") == 0
+ || ACE_OS::strcmp (arg, "IV") == 0
+ || ACE_OS::strcmp (arg, "Pro") == 0)
+ {
+ supported = 1;
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" recognized Pentium Pro/II chip...")));
+ }
+ }
+ else if (::sscanf (buf, "cpu MHz : %lf\n", &mhertz) == 1)
+ {
+ // If the line "cpu MHz : xxx" is present, then it's a
+ // reliable measure of the CPU speed - according to the
+ // kernel-source.
+ scale_factor = (ACE_UINT32) (mhertz + 0.5);
+ break;
+ }
+ else if (::sscanf (buf, "bogomips : %lf\n", &bmips) == 1
+ || ::sscanf (buf, "BogoMIPS : %lf\n", &bmips) == 1)
+ {
+ if (supported)
+ {
+ scale_factor = (ACE_UINT32) (bmips + 0.5);
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" setting the clock scale factor to %u"), scale_factor));
+ }
+#if 0
+ else
+ {
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_LIB_TEXT ("\nThe BogoMIPS metric is not supported on this platform"
+ "\n\tReport the results of the clock calibration and"
+ "\n\tthe contents of /proc/cpuinfo to the ace-users mailing list")));
+ }
+#endif /* 0 */
+ break;
+ }
+#endif /* __alpha__ */
+ }
+
+ // ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (done)\n")));
+
+ ACE_OS::fclose (cpuinfo);
+ }
+
+ return scale_factor;
+}
+#endif /* linux */
+
+ACE_UINT32
+ACE_High_Res_Timer::global_scale_factor (void)
+{
+#if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
+ defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
+ !defined (ACE_HAS_HI_RES_TIMER) && \
+ ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \
+ defined (ghs) || defined (__GNUG__) || defined (__KCC) || \
+ defined (__INTEL_COMPILER))
+ // Check if the global scale factor needs to be set, and do if so.
+ if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
+ {
+ // Grab ACE's static object lock. This doesn't have anything to
+ // do with static objects; it's just a convenient lock to use.
+ ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon,
+ *ACE_Static_Object_Lock::instance (), 0));
+
+ // Double check
+ if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
+ {
+# if defined (ACE_WIN32)
+ LARGE_INTEGER freq;
+ if (::QueryPerformanceFrequency (&freq))
+ {
+ // We have a high-res timer
+# if defined (ACE_LACKS_LONGLONG_T)
+ ACE_UINT64 uint64_freq(freq.u.LowPart, (ACE_UINT32) freq.u.HighPart);
+ ACE_High_Res_Timer::global_scale_factor
+ (uint64_freq / (ACE_UINT32) ACE_ONE_SECOND_IN_USECS);
+# else
+ ACE_High_Res_Timer::global_scale_factor
+ (static_cast<unsigned int> (freq.QuadPart / ACE_HR_SCALE_CONVERSION));
+# endif // (ACE_LACKS_LONGLONG_T)
+
+ ACE_High_Res_Timer::global_scale_factor_status_ = 1;
+ }
+ else
+ // High-Res timers not supported
+ ACE_High_Res_Timer::global_scale_factor_status_ = -1;
+
+ return ACE_High_Res_Timer::global_scale_factor_;
+
+# elif defined (linux)
+ ACE_High_Res_Timer::global_scale_factor (ACE_High_Res_Timer::get_cpuinfo ());
+# endif /* ! ACE_WIN32 && ! (linux && __alpha__) */
+
+# if !defined (ACE_WIN32)
+ if (ACE_High_Res_Timer::global_scale_factor_ == 1u)
+ // Failed to retrieve CPU speed from system, so calculate it.
+ ACE_High_Res_Timer::calibrate ();
+# endif // (ACE_WIN32)
+ }
+ }
+
+ ACE_High_Res_Timer::global_scale_factor_status_ = 1;
+#endif /* (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
+ ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) && \
+ ! ACE_HAS_HI_RES_TIMER &&
+ ((WIN32 && ! WINCE) || ghs || __GNUG__) */
+
+ return ACE_High_Res_Timer::global_scale_factor_;
+}
+
+ACE_High_Res_Timer::ACE_High_Res_Timer (void)
+{
+ ACE_TRACE ("ACE_High_Res_Timer::ACE_High_Res_Timer");
+
+ this->reset ();
+
+ // Make sure that the global scale factor is set.
+ (void) global_scale_factor ();
+}
+
+ACE_UINT32
+ACE_High_Res_Timer::calibrate (const ACE_UINT32 usec,
+ const u_int iterations)
+{
+ const ACE_Time_Value sleep_time (0, usec);
+ ACE_Stats delta_hrtime;
+ // In units of 100 usec, to avoid overflow.
+ ACE_Stats actual_sleeps;
+
+ for (u_int i = 0;
+ i < iterations;
+ ++i)
+ {
+ const ACE_Time_Value actual_start =
+ ACE_OS::gettimeofday ();
+ const ACE_hrtime_t start =
+ ACE_OS::gethrtime ();
+ ACE_OS::sleep (sleep_time);
+ const ACE_hrtime_t stop =
+ ACE_OS::gethrtime ();
+ const ACE_Time_Value actual_delta =
+ ACE_OS::gettimeofday () - actual_start;
+
+ // Store the sample.
+ delta_hrtime.sample (ACE_HRTIME_CONVERSION (stop - start));
+ actual_sleeps.sample (actual_delta.msec () * 100u);
+ }
+
+ // Calculate the mean value of the samples, with no fractional
+ // precision. Use it for the global scale factor.
+ ACE_Stats_Value ticks (0);
+ delta_hrtime.mean (ticks);
+
+ ACE_Stats_Value actual_sleep (0);
+ actual_sleeps.mean (actual_sleep);
+
+ // The addition of 5 below rounds instead of truncates.
+ const ACE_UINT32 scale_factor =
+ (ticks.whole () / actual_sleep.whole () + 5) /
+ 10u /* usec/100 usec */;
+ ACE_High_Res_Timer::global_scale_factor (scale_factor);
+
+ return scale_factor;
+}
+
+void
+ACE_High_Res_Timer::dump (void) const
+{
+#if defined (ACE_HAS_DUMP)
+ ACE_TRACE ("ACE_High_Res_Timer::dump");
+
+ ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
+ ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nglobal_scale_factor_: %u\n"),
+ global_scale_factor ()));
+#if defined (ACE_LACKS_LONGLONG_T)
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_LIB_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
+ start_.hi (), start_.lo (),
+ end_.hi (), end_.lo (),
+ total_.hi (), total_.lo (),
+ start_incr_.hi (), start_incr_.lo ()));
+#else /* ! ACE_LACKS_LONGLONG_T */
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_LIB_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n")
+ ACE_LIB_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
+ ACE_CU64_TO_CU32 (start_ >> 32),
+ ACE_CU64_TO_CU32 (start_ & 0xfffffffful),
+ ACE_CU64_TO_CU32 (end_ >> 32),
+ ACE_CU64_TO_CU32 (end_ & 0xfffffffful),
+ ACE_CU64_TO_CU32 (total_ >> 32),
+ ACE_CU64_TO_CU32 (total_ & 0xfffffffful),
+ ACE_CU64_TO_CU32 (start_incr_ >> 32),
+ ACE_CU64_TO_CU32 (start_incr_ & 0xfffffffful)));
+#endif /* ! ACE_LACKS_LONGLONG_T */
+ ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP));
+#endif /* ACE_HAS_DUMP */
+}
+
+void
+ACE_High_Res_Timer::reset (void)
+{
+ ACE_TRACE ("ACE_High_Res_Timer::reset");
+
+ this->start_ = 0;
+ this->end_ = 0;
+ this->total_ = 0;
+ this->start_incr_ = 0;
+}
+
+void
+ACE_High_Res_Timer::elapsed_time (ACE_Time_Value &tv) const
+{
+ hrtime_to_tv (tv,
+ ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_));
+}
+
+#if defined (ACE_HAS_POSIX_TIME)
+// Note... Win32 does not have ACE_HAS_POSIX_TIME, so the scale factor
+// does not need to take into account the different units on Win32.
+
+void
+ACE_High_Res_Timer::elapsed_time (struct timespec &elapsed_time) const
+{
+ // This implementation should be cleaned up.
+
+ // Just grab the nanoseconds. That is, leave off all values above
+ // microsecond. This equation is right! Don't mess with me! (It
+ // first strips off everything but the portion less than 1 usec.
+ // Then it converts that to nanoseconds by dividing by the scale
+ // factor to convert to usec, and multiplying by 1000.) The cast
+ // avoids a MSVC 4.1 compiler warning about narrowing.
+ ACE_hrtime_t elapsed =
+ ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_);
+ u_long nseconds = static_cast<u_long> (elapsed %
+ global_scale_factor () * 1000u /
+ global_scale_factor ());
+
+ // Get just the microseconds (dropping any left over nanoseconds).
+ ACE_UINT32 useconds = (ACE_UINT32) (elapsed / global_scale_factor ());
+
+ elapsed_time.tv_sec = (time_t) (useconds / ACE_ONE_SECOND_IN_USECS);
+ // Transforms one second in microseconds into nanoseconds.
+ elapsed_time.tv_nsec = (time_t) ((useconds % ACE_ONE_SECOND_IN_USECS) * 1000u + nseconds);
+}
+#endif /* ACE_HAS_POSIX_TIME */
+
+void
+ACE_High_Res_Timer::elapsed_time_incr (ACE_Time_Value &tv) const
+{
+ hrtime_to_tv (tv, total_);
+}
+
+void
+ACE_High_Res_Timer::elapsed_time (ACE_hrtime_t &nanoseconds) const
+{
+ // Please do _not_ rearrange this equation. It is carefully
+ // designed and tested to avoid overflow on machines that don't have
+ // native 64-bit ints. In particular, division can be a problem.
+ // For more background on this, please see bugzilla #1024.
+#if defined (ACE_WIN32)
+ nanoseconds = ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_)
+ * (1024000000u / ACE_High_Res_Timer::global_scale_factor());
+#else
+ nanoseconds = ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_)
+ * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
+#endif /* ACE_WIN32 */
+ // Caution - Borland has a problem with >>=, so resist the temptation.
+ nanoseconds = nanoseconds >> 10;
+ // Right shift is implemented for non native 64-bit ints
+ // operator/ only for a 32 bit result !
+}
+
+void
+ACE_High_Res_Timer::elapsed_time_incr (ACE_hrtime_t &nanoseconds) const
+{
+ // Same as above.
+#if defined (ACE_WIN32)
+ nanoseconds = this->total_
+ * (1024000000u / ACE_High_Res_Timer::global_scale_factor());
+#else
+ nanoseconds = this->total_
+ * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
+#endif
+ // Caution - Borland has a problem with >>=, so resist the temptation.
+ nanoseconds = nanoseconds >> 10;
+}
+
+#if !defined (ACE_HAS_WINCE)
+void
+ACE_High_Res_Timer::print_ave (const ACE_TCHAR *str,
+ const int count,
+ ACE_HANDLE handle) const
+{
+ ACE_TRACE ("ACE_High_Res_Timer::print_ave");
+
+ // Get the total number of nanoseconds elapsed.
+ ACE_hrtime_t total_nanoseconds;
+ this->elapsed_time (total_nanoseconds);
+
+ // Separate to seconds and nanoseconds.
+ u_long total_secs =
+ static_cast<u_long> (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
+ ACE_UINT32 extra_nsecs =
+ static_cast<ACE_UINT32> (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
+
+ ACE_TCHAR buf[100];
+ if (count > 1)
+ {
+ ACE_hrtime_t avg_nsecs = total_nanoseconds / (ACE_UINT32) count;
+ ACE_OS::sprintf (buf,
+ ACE_LIB_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
+ count,
+ total_secs,
+ (extra_nsecs + 500u) / 1000u,
+ (u_long) ((avg_nsecs + 500u) / 1000u));
+ }
+ else
+ ACE_OS::sprintf (buf,
+ ACE_LIB_TEXT (" total %3lu.%06lu secs\n"),
+ total_secs,
+ (extra_nsecs + 500lu) / 1000lu);
+
+ ACE_OS::write (handle,
+ str,
+ ACE_OS::strlen (str));
+ ACE_OS::write (handle,
+ buf,
+ ACE_OS::strlen (buf));
+}
+
+void
+ACE_High_Res_Timer::print_total (const ACE_TCHAR *str,
+ const int count,
+ ACE_HANDLE handle) const
+{
+ ACE_TRACE ("ACE_High_Res_Timer::print_total");
+
+ // Get the total number of nanoseconds elapsed.
+ ACE_hrtime_t total_nanoseconds;
+ this->elapsed_time (total_nanoseconds);
+
+ // Separate to seconds and nanoseconds.
+ u_long total_secs =
+ (u_long) (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
+ ACE_UINT32 extra_nsecs =
+ (ACE_UINT32) (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
+
+ ACE_TCHAR buf[100];
+ if (count > 1)
+ {
+ ACE_hrtime_t avg_nsecs = this->total_ / (ACE_UINT32) count;
+
+ ACE_OS::sprintf (buf,
+ ACE_LIB_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
+ count,
+ total_secs,
+ (extra_nsecs + 500u) / 1000u,
+ (u_long) ((avg_nsecs + 500u) / 1000u));
+ }
+ else
+ ACE_OS::sprintf (buf,
+ ACE_LIB_TEXT (" total %3lu.%06u secs\n"),
+ total_secs,
+ (extra_nsecs + 500u) / 1000u);
+
+ ACE_OS::write (handle,
+ str,
+ ACE_OS::strlen (str));
+ ACE_OS::write (handle,
+ buf,
+ ACE_OS::strlen (buf));
+}
+#endif /* !ACE_HAS_WINCE */
+
+int
+ACE_High_Res_Timer::get_env_global_scale_factor (const ACE_TCHAR *env)
+{
+#if !defined (ACE_HAS_WINCE)
+ if (env != 0)
+ {
+ const char *env_value = ACE_OS::getenv (ACE_TEXT_ALWAYS_CHAR (env));
+ if (env_value != 0)
+ {
+ int value = ACE_OS::atoi (env_value);
+ if (value > 0)
+ {
+ ACE_High_Res_Timer::global_scale_factor (value);
+ return 0;
+ }
+ }
+ }
+#else
+ ACE_UNUSED_ARG (env);
+#endif /* !ACE_HAS_WINCE */
+ return -1;
+}
+
+ACE_END_VERSIONED_NAMESPACE_DECL