Also use Objects as part of an operation but as a result don't generate Any operation...
[ACE_TAO.git] / ACE / ace / High_Res_Timer.cpp
blob048cb4d63878e653af0da4ec500846c0a987f1aa
1 // Be very carefull before changing the calculations inside
2 // ACE_High_Res_Timer. The precision matters and we are using integer
3 // calculations not floating point. Also look good at the emulated 64
4 // bit int class (inside Basic_Types{h,i,cpp} before changing
5 // anything. It's operator/ only returns 32 bits not 64 bits, among
6 // other things.
8 #include "ace/High_Res_Timer.h"
10 #if !defined (__ACE_INLINE__)
11 #include "ace/High_Res_Timer.inl"
12 #endif /* __ACE_INLINE__ */
14 #include "ace/Stats.h"
15 #include "ace/OS_NS_stdio.h"
16 #include "ace/OS_NS_string.h"
17 #include "ace/OS_NS_sys_time.h"
18 #include "ace/OS_NS_time.h"
19 #include "ace/OS_NS_unistd.h"
20 #include "ace/OS_NS_stdlib.h"
21 #include "ace/Truncate.h"
23 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
25 ACE_ALLOC_HOOK_DEFINE(ACE_High_Res_Timer)
27 ACE_END_VERSIONED_NAMESPACE_DECL
29 // For Intel platforms, a scale factor is required for
30 // ACE_OS::gethrtime. We'll still set this to one to prevent division
31 // by zero errors.
32 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
33 defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
34 !defined (ACE_HAS_HI_RES_TIMER)
36 # include "ace/Guard_T.h"
37 # include "ace/Recursive_Thread_Mutex.h"
38 # include "ace/Object_Manager.h"
40 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
42 /// Initialize the global_scale_factor_ to 1. The first
43 /// ACE_High_Res_Timer instance construction will override this
44 /// value.
45 /* static */
46 ACE_High_Res_Timer::global_scale_factor_type ACE_High_Res_Timer::global_scale_factor_ = 1u;
48 ACE_END_VERSIONED_NAMESPACE_DECL
50 #else /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
51 ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) ||
52 ACE_HAS_HI_RES_TIMER */
54 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
56 // A scale_factor of 1000 converts nanosecond ticks to microseconds.
57 // That is, on these platforms, 1 tick == 1 nanosecond.
58 /* static */
59 ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1000u;
61 ACE_END_VERSIONED_NAMESPACE_DECL
62 #endif /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
63 ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) ||
64 ACE_HAS_HI_RES_TIMER */
66 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
68 /// This is used to tell if the global_scale_factor_ has been
69 /// set, and if high resolution timers are supported.
70 /* static */
71 int ACE_High_Res_Timer::global_scale_factor_status_ = 0;
73 #if defined (ACE_LINUX) && !defined (ACE_LACKS_SSCANF)
74 // Determine the apparent CPU clock speed from /proc/cpuinfo
75 ACE_UINT32
76 ACE_High_Res_Timer::get_cpuinfo (void)
78 ACE_UINT32 scale_factor = 1u;
80 // Get the BogoMIPS from /proc/cpuinfo. It works fine on Alpha and
81 // Pentium Pro. For other CPUs, it will be necessary to interpret
82 // the BogoMips, as described in the BogoMips mini-HOWTO. Note that
83 // this code assumes an order to the /proc/cpuinfo contents. The
84 // BogoMips rating had better come after CPU type and model info.
85 #if !defined (__alpha__)
86 bool supported = false;
87 #endif /* __alpha__ */
89 FILE *cpuinfo = ACE_OS::fopen (ACE_TEXT ("/proc/cpuinfo"),
90 ACE_TEXT ("r"));
92 if (cpuinfo != 0)
94 char buf[128];
96 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("\nReading /proc/cpuinfo...")));
98 while (ACE_OS::fgets (buf, sizeof buf, cpuinfo))
100 #if defined (__alpha__)
101 ACE_UINT32 whole;
102 ACE_UINT32 fractional;
103 if (::sscanf (buf,
104 "BogoMIPS : %d.%d\n",
105 &whole,
106 &fractional) == 2
107 || ::sscanf (buf,
108 "bogomips : %d.%d\n",
109 &whole,
110 &fractional) == 2)
112 scale_factor = whole;
113 break;
115 #else
116 double mhertz = 1;
117 double bmips = 1;
118 char arg[128];
120 // CPU type?
121 if (::sscanf (buf, "cpu : %s\n", arg) == 1)
123 // If this is an Alpha chip, then the BogoMips rating is
124 // usable...
125 if (ACE_OS::strncmp (arg,
126 "Alpha",
127 5) == 0)
129 supported = true;
132 // Pentium CPU model?
133 else if (!supported
134 && ::sscanf (buf, "model name : Pentium %s\n", arg) == 1)
136 // But if we don't have the right kind of Intel chip,
137 // just quit.
138 if (ACE_OS::strcmp (arg, "II") == 0
139 || ACE_OS::strcmp (arg, "III") == 0
140 || ACE_OS::strcmp (arg, "IV") == 0
141 || ACE_OS::strcmp (arg, "Pro") == 0)
143 supported = true;
146 else if (::sscanf (buf, "cpu MHz : %lf\n", &mhertz) == 1)
148 // If the line "cpu MHz : xxx" is present, then it's a
149 // reliable measure of the CPU speed - according to the
150 // kernel-source. It's possible to see a 0 value reported.
151 if (mhertz > 0.0)
153 scale_factor = (ACE_UINT32) (mhertz + 0.5);
154 break;
157 else if (::sscanf (buf, "bogomips : %lf\n", &bmips) == 1
158 || ::sscanf (buf, "BogoMIPS : %lf\n", &bmips) == 1)
160 if (supported)
162 scale_factor = (ACE_UINT32) (bmips + 0.5);
163 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" setting the clock scale factor to %u"), scale_factor));
165 #if 0
166 else
168 ACELIB_DEBUG ((LM_DEBUG,
169 ACE_TEXT ("\nThe BogoMIPS metric is not supported on this platform"
170 "\n\tReport the results of the clock calibration and"
171 "\n\tthe contents of /proc/cpuinfo to the ace-users mailing list")));
173 #endif /* 0 */
174 break;
176 #endif /* __alpha__ */
179 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" (done)\n")));
181 ACE_OS::fclose (cpuinfo);
184 return scale_factor;
186 #endif /* ACE_LINUX && !ACE_LACKS_SSCANF*/
188 ACE_High_Res_Timer::global_scale_factor_type
189 ACE_High_Res_Timer::global_scale_factor (void)
191 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
192 defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
193 !defined (ACE_HAS_HI_RES_TIMER) && \
194 (defined (ACE_WIN32) || \
195 defined (ghs) || defined (__GNUG__) || \
196 defined (__INTEL_COMPILER))
197 // Check if the global scale factor needs to be set, and do if so.
198 if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
200 // Grab ACE's static object lock. This doesn't have anything to
201 // do with static objects; it's just a convenient lock to use.
202 ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon,
203 *ACE_Static_Object_Lock::instance (), 0));
205 // Double check
206 if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
208 # if defined (ACE_WIN32)
209 // This a higher-precision version, specific for Windows systems
210 LARGE_INTEGER freq;
211 if (::QueryPerformanceFrequency (&freq))
213 ACE_High_Res_Timer::global_scale_factor(freq.QuadPart);
215 // Succeeded in setting the global scale factor
216 ACE_High_Res_Timer::global_scale_factor_status_ = 1;
218 else
220 // High-Res timers not supported
221 ACE_High_Res_Timer::global_scale_factor_status_ = -1;
223 # elif defined (ACE_LINUX)
224 # if defined (ACE_LACKS_SSCANF)
225 ACE_High_Res_Timer::global_scale_factor (1);
226 # else
227 ACE_High_Res_Timer::global_scale_factor (ACE_High_Res_Timer::get_cpuinfo ());
228 # endif /* ACE_LACKS_SSCANF */
229 # endif /* ! ACE_WIN32 && ! (ACE_LINUX && __alpha__) */
231 # if !defined (ACE_WIN32)
232 if (ACE_High_Res_Timer::global_scale_factor_ <= 1u)
233 // Failed to retrieve CPU speed from system, so calculate it.
234 ACE_High_Res_Timer::calibrate ();
236 // We have set the global scale factor so set out status
237 // to 1, this way we only set the global_scale_factor_ once.
238 // Must be in the !ACE_WIN32 block to make sure that we
239 // not change a value of -1 (only done for ACE_WINC32) by accident
240 // to 1
241 ACE_High_Res_Timer::global_scale_factor_status_ = 1;
242 # endif // (ACE_WIN32)
246 #endif /* (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
247 ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) && \
248 ! ACE_HAS_HI_RES_TIMER &&
249 ((WIN32 && ! WINCE) || ghs || __GNUG__) */
251 return ACE_High_Res_Timer::global_scale_factor_;
254 ACE_High_Res_Timer::ACE_High_Res_Timer (void)
256 ACE_TRACE ("ACE_High_Res_Timer::ACE_High_Res_Timer");
258 this->reset ();
260 // Make sure that the global scale factor is set.
261 (void) global_scale_factor ();
264 ACE_UINT32
265 ACE_High_Res_Timer::calibrate (const ACE_UINT32 usec,
266 const u_int iterations)
268 const ACE_Time_Value sleep_time (0, usec);
269 ACE_Stats delta_hrtime;
270 // In units of 100 usec, to avoid overflow.
271 ACE_Stats actual_sleeps;
273 for (u_int i = 0;
274 i < iterations;
275 ++i)
277 ACE_Time_Value const actual_start = ACE_OS::gettimeofday ();
278 ACE_hrtime_t const start = ACE_OS::gethrtime ();
279 ACE_OS::sleep (sleep_time);
280 ACE_hrtime_t const stop = ACE_OS::gethrtime ();
281 ACE_Time_Value const actual_delta =
282 ACE_OS::gettimeofday () - actual_start;
284 // Store the sample.
285 delta_hrtime.sample (ACE_Utils::truncate_cast<ACE_INT32> (stop - start));
286 actual_sleeps.sample (actual_delta.msec () * 100u);
289 // Calculate the mean value of the samples, with no fractional
290 // precision. Use it for the global scale factor.
291 ACE_Stats_Value ticks (0);
292 delta_hrtime.mean (ticks);
294 ACE_Stats_Value actual_sleep (0);
295 actual_sleeps.mean (actual_sleep);
297 // The addition of 5 below rounds instead of truncates.
298 const ACE_UINT32 scale_factor =
299 (ticks.whole () / actual_sleep.whole () + 5) /
300 10u /* usec/100 usec */;
301 ACE_High_Res_Timer::global_scale_factor (scale_factor);
303 return scale_factor;
306 void
307 ACE_High_Res_Timer::dump (void) const
309 #if defined (ACE_HAS_DUMP)
310 ACE_TRACE ("ACE_High_Res_Timer::dump");
312 ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
313 ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("\nglobal_scale_factor_: %u\n"),
314 global_scale_factor ()));
315 ACELIB_DEBUG ((LM_DEBUG,
316 ACE_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n")
317 ACE_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n")
318 ACE_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n")
319 ACE_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
320 static_cast<ACE_UINT32> (start_ >> 32),
321 static_cast<ACE_UINT32> (start_ & 0xfffffffful),
322 static_cast<ACE_UINT32> (end_ >> 32),
323 static_cast<ACE_UINT32> (end_ & 0xfffffffful),
324 static_cast<ACE_UINT32> (total_ >> 32),
325 static_cast<ACE_UINT32> (total_ & 0xfffffffful),
326 static_cast<ACE_UINT32> (start_incr_ >> 32),
327 static_cast<ACE_UINT32> (start_incr_ & 0xfffffffful)));
328 ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP));
329 #endif /* ACE_HAS_DUMP */
332 void
333 ACE_High_Res_Timer::reset (void)
335 ACE_TRACE ("ACE_High_Res_Timer::reset");
337 this->start_ = 0;
338 this->end_ = 0;
339 this->total_ = 0;
340 this->start_incr_ = 0;
343 void
344 ACE_High_Res_Timer::elapsed_time (ACE_Time_Value &tv) const
346 this->hrtime_to_tv (tv,
347 ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_));
350 #if defined (ACE_HAS_POSIX_TIME)
351 // Note... Win32 does not have ACE_HAS_POSIX_TIME, so the scale factor
352 // does not need to take into account the different units on Win32.
354 void
355 ACE_High_Res_Timer::elapsed_time (struct timespec &elapsed_time) const
357 // This implementation should be cleaned up.
359 // Just grab the nanoseconds. That is, leave off all values above
360 // microsecond. This equation is right! Don't mess with me! (It
361 // first strips off everything but the portion less than 1 usec.
362 // Then it converts that to nanoseconds by dividing by the scale
363 // factor to convert to usec, and multiplying by 1000.) The cast
364 // avoids a MSVC 4.1 compiler warning about narrowing.
365 ACE_hrtime_t elapsed =
366 ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_);
367 u_long nseconds = static_cast<u_long> (elapsed %
368 global_scale_factor () * 1000u /
369 global_scale_factor ());
371 // Get just the microseconds (dropping any left over nanoseconds).
372 ACE_UINT32 useconds = (ACE_UINT32) (elapsed / global_scale_factor ());
374 elapsed_time.tv_sec = (time_t) (useconds / ACE_ONE_SECOND_IN_USECS);
375 // Transforms one second in microseconds into nanoseconds.
376 elapsed_time.tv_nsec = (time_t) ((useconds % ACE_ONE_SECOND_IN_USECS) * 1000u + nseconds);
378 #endif /* ACE_HAS_POSIX_TIME */
380 void
381 ACE_High_Res_Timer::elapsed_time_incr (ACE_Time_Value &tv) const
383 this->hrtime_to_tv (tv, total_);
386 void
387 ACE_High_Res_Timer::elapsed_time (ACE_hrtime_t &nanoseconds) const
389 #if !defined (ACE_WIN32)
390 // Please do _not_ rearrange this equation. It is carefully
391 // designed and tested to avoid overflow on machines that don't have
392 // native 64-bit ints. In particular, division can be a problem.
393 // For more background on this, please see bugzilla #1024.
394 nanoseconds = ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_)
395 * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
396 // Caution - Borland has a problem with >>=, so resist the temptation.
397 nanoseconds = nanoseconds >> 10;
398 // Right shift is implemented for non native 64-bit ints
399 // operator/ only for a 32 bit result !
400 #else
401 // This a higher-precision version, specific for Windows systems
402 nanoseconds =
403 (ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_) * ACE_HR_SCALE_CONVERSION * 1000u) /
404 ACE_High_Res_Timer::global_scale_factor ();
405 #endif
408 void
409 ACE_High_Res_Timer::elapsed_time_incr (ACE_hrtime_t &nanoseconds) const
411 #if !defined (ACE_WIN32)
412 // Same as above.
413 nanoseconds = this->total_
414 * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
415 // Caution - Borland has a problem with >>=, so resist the temptation.
416 nanoseconds = nanoseconds >> 10;
417 #else
418 // This a higher-precision version, specific for Windows systems
419 nanoseconds =
420 this->total_ * 1000000000u /
421 ACE_High_Res_Timer::global_scale_factor ();
422 #endif
425 void
426 ACE_High_Res_Timer::print_ave (const ACE_TCHAR *str,
427 const int count,
428 ACE_HANDLE handle) const
430 ACE_TRACE ("ACE_High_Res_Timer::print_ave");
432 // Get the total number of nanoseconds elapsed.
433 ACE_hrtime_t total_nanoseconds;
434 this->elapsed_time (total_nanoseconds);
436 // Separate to seconds and nanoseconds.
437 u_long total_secs =
438 static_cast<u_long> (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
439 ACE_UINT32 extra_nsecs =
440 static_cast<ACE_UINT32> (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
442 ACE_TCHAR buf[100];
443 if (count > 1)
445 ACE_hrtime_t avg_nsecs = total_nanoseconds / (ACE_UINT32) count;
446 ACE_OS::snprintf (buf, 100,
447 ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
448 count,
449 total_secs,
450 (extra_nsecs + 500u) / 1000u,
451 (u_long) ((avg_nsecs + 500u) / 1000u));
453 else
454 ACE_OS::snprintf (buf, 100,
455 ACE_TEXT (" total %3lu.%06lu secs\n"),
456 total_secs,
457 (extra_nsecs + 500lu) / 1000lu);
459 ACE_OS::write (handle,
460 str,
461 ACE_OS::strlen (str));
462 ACE_OS::write (handle,
463 buf,
464 ACE_OS::strlen (buf));
467 void
468 ACE_High_Res_Timer::print_total (const ACE_TCHAR *str,
469 const int count,
470 ACE_HANDLE handle) const
472 ACE_TRACE ("ACE_High_Res_Timer::print_total");
474 // Get the total number of nanoseconds elapsed.
475 ACE_hrtime_t total_nanoseconds;
476 this->elapsed_time (total_nanoseconds);
478 // Separate to seconds and nanoseconds.
479 u_long total_secs =
480 static_cast<u_long> (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
481 ACE_UINT32 extra_nsecs =
482 static_cast<ACE_UINT32> (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
484 ACE_TCHAR buf[100];
485 if (count > 1)
487 ACE_hrtime_t avg_nsecs = this->total_ / (ACE_UINT32) count;
489 ACE_OS::snprintf (buf, 100,
490 ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
491 count,
492 total_secs,
493 (extra_nsecs + 500u) / 1000u,
494 (u_long) ((avg_nsecs + 500u) / 1000u));
496 else
497 ACE_OS::snprintf (buf, 100,
498 ACE_TEXT (" total %3lu.%06u secs\n"),
499 total_secs,
500 (extra_nsecs + 500u) / 1000u);
502 ACE_OS::write (handle,
503 str,
504 ACE_OS::strlen (str));
505 ACE_OS::write (handle,
506 buf,
507 ACE_OS::strlen (buf));
511 ACE_High_Res_Timer::get_env_global_scale_factor (const ACE_TCHAR *env)
513 if (env != 0)
515 const char *env_value = ACE_OS::getenv (ACE_TEXT_ALWAYS_CHAR (env));
516 if (env_value != 0)
518 int const value = ACE_OS::atoi (env_value);
519 if (value > 0)
521 ACE_High_Res_Timer::global_scale_factor (value);
522 return 0;
527 return -1;
530 ACE_END_VERSIONED_NAMESPACE_DECL