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
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
32 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
33 defined (ACE_HAS_PENTIUM)) && \
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
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 || \
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.
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 || \
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.
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
76 ACE_High_Res_Timer::get_cpuinfo ()
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 bool supported
= false;
87 FILE *cpuinfo
= ACE_OS::fopen (ACE_TEXT ("/proc/cpuinfo"),
94 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("\nReading /proc/cpuinfo...")));
96 while (ACE_OS::fgets (buf
, sizeof buf
, cpuinfo
))
103 if (::sscanf (buf
, "cpu : %s\n", arg
) == 1)
105 // If this is an Alpha chip, then the BogoMips rating is
107 if (ACE_OS::strncmp (arg
,
114 // Pentium CPU model?
116 && ::sscanf (buf
, "model name : Pentium %s\n", arg
) == 1)
118 // But if we don't have the right kind of Intel chip,
120 if (ACE_OS::strcmp (arg
, "II") == 0
121 || ACE_OS::strcmp (arg
, "III") == 0
122 || ACE_OS::strcmp (arg
, "IV") == 0
123 || ACE_OS::strcmp (arg
, "Pro") == 0)
128 else if (::sscanf (buf
, "cpu MHz : %lf\n", &mhertz
) == 1)
130 // If the line "cpu MHz : xxx" is present, then it's a
131 // reliable measure of the CPU speed - according to the
132 // kernel-source. It's possible to see a 0 value reported.
135 scale_factor
= (ACE_UINT32
) (mhertz
+ 0.5);
139 else if (::sscanf (buf
, "bogomips : %lf\n", &bmips
) == 1
140 || ::sscanf (buf
, "BogoMIPS : %lf\n", &bmips
) == 1)
144 scale_factor
= (ACE_UINT32
) (bmips
+ 0.5);
145 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" setting the clock scale factor to %u"), scale_factor));
150 ACELIB_DEBUG ((LM_DEBUG
,
151 ACE_TEXT ("\nThe BogoMIPS metric is not supported on this platform"
152 "\n\tReport the results of the clock calibration and"
153 "\n\tthe contents of /proc/cpuinfo to the ACE github project")));
160 // ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" (done)\n")));
162 ACE_OS::fclose (cpuinfo
);
167 #endif /* ACE_LINUX && !ACE_LACKS_SSCANF*/
169 ACE_High_Res_Timer::global_scale_factor_type
170 ACE_High_Res_Timer::global_scale_factor ()
172 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
173 defined (ACE_HAS_PENTIUM)) && \
174 !defined (ACE_HAS_HI_RES_TIMER) && \
175 (defined (ACE_WIN32) || \
176 defined (ghs) || defined (__GNUG__) || \
177 defined (__INTEL_COMPILER))
178 // Check if the global scale factor needs to be set, and do if so.
179 if (ACE_High_Res_Timer::global_scale_factor_status_
== 0)
181 // Grab ACE's static object lock. This doesn't have anything to
182 // do with static objects; it's just a convenient lock to use.
183 ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex
, ace_mon
,
184 *ACE_Static_Object_Lock::instance (), 0));
187 if (ACE_High_Res_Timer::global_scale_factor_status_
== 0)
189 # if defined (ACE_WIN32)
190 // This a higher-precision version, specific for Windows systems
192 if (::QueryPerformanceFrequency (&freq
))
194 ACE_High_Res_Timer::global_scale_factor(freq
.QuadPart
);
196 // Succeeded in setting the global scale factor
197 ACE_High_Res_Timer::global_scale_factor_status_
= 1;
201 // High-Res timers not supported
202 ACE_High_Res_Timer::global_scale_factor_status_
= -1;
204 # elif defined (ACE_LINUX)
205 # if defined (ACE_LACKS_SSCANF)
206 ACE_High_Res_Timer::global_scale_factor (1);
208 ACE_High_Res_Timer::global_scale_factor (ACE_High_Res_Timer::get_cpuinfo ());
209 # endif /* ACE_LACKS_SSCANF */
210 # endif /* !ACE_WIN32 && !ACE_LINUX */
212 # if !defined (ACE_WIN32)
213 if (ACE_High_Res_Timer::global_scale_factor_
<= 1u)
214 // Failed to retrieve CPU speed from system, so calculate it.
215 ACE_High_Res_Timer::calibrate ();
217 // We have set the global scale factor so set out status
218 // to 1, this way we only set the global_scale_factor_ once.
219 // Must be in the !ACE_WIN32 block to make sure that we
220 // not change a value of -1 (only done for ACE_WINC32) by accident
222 ACE_High_Res_Timer::global_scale_factor_status_
= 1;
223 # endif // (ACE_WIN32)
227 #endif /* (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
228 ACE_HAS_PENTIUM) && \
229 ! ACE_HAS_HI_RES_TIMER &&
230 ((WIN32) || ghs || __GNUG__) */
232 return ACE_High_Res_Timer::global_scale_factor_
;
235 ACE_High_Res_Timer::ACE_High_Res_Timer ()
237 ACE_TRACE ("ACE_High_Res_Timer::ACE_High_Res_Timer");
241 // Make sure that the global scale factor is set.
242 (void) global_scale_factor ();
246 ACE_High_Res_Timer::calibrate (const ACE_UINT32 usec
,
247 const u_int iterations
)
249 const ACE_Time_Value
sleep_time (0, usec
);
250 ACE_Stats delta_hrtime
;
251 // In units of 100 usec, to avoid overflow.
252 ACE_Stats actual_sleeps
;
258 ACE_Time_Value
const actual_start
= ACE_OS::gettimeofday ();
259 ACE_hrtime_t
const start
= ACE_OS::gethrtime ();
260 ACE_OS::sleep (sleep_time
);
261 ACE_hrtime_t
const stop
= ACE_OS::gethrtime ();
262 ACE_Time_Value
const actual_delta
=
263 ACE_OS::gettimeofday () - actual_start
;
266 delta_hrtime
.sample (ACE_Utils::truncate_cast
<ACE_INT32
> (stop
- start
));
267 actual_sleeps
.sample (actual_delta
.msec () * 100u);
270 // Calculate the mean value of the samples, with no fractional
271 // precision. Use it for the global scale factor.
272 ACE_Stats_Value
ticks (0);
273 delta_hrtime
.mean (ticks
);
275 ACE_Stats_Value
actual_sleep (0);
276 actual_sleeps
.mean (actual_sleep
);
278 // The addition of 5 below rounds instead of truncates.
279 const ACE_UINT32 scale_factor
=
280 (ticks
.whole () / actual_sleep
.whole () + 5) /
281 10u /* usec/100 usec */;
282 ACE_High_Res_Timer::global_scale_factor (scale_factor
);
288 ACE_High_Res_Timer::dump () const
290 #if defined (ACE_HAS_DUMP)
291 ACE_TRACE ("ACE_High_Res_Timer::dump");
293 ACELIB_DEBUG ((LM_DEBUG
, ACE_BEGIN_DUMP
, this));
294 ACELIB_DEBUG ((LM_DEBUG
, ACE_TEXT ("\nglobal_scale_factor_: %u\n"),
295 global_scale_factor ()));
296 ACELIB_DEBUG ((LM_DEBUG
,
297 ACE_TEXT (":\nstart_.hi (): %8x; start_.lo (): %8x;\n")
298 ACE_TEXT ("end_.hi (): %8x; end_.lo (): %8x;\n")
299 ACE_TEXT ("total_.hi (): %8x; total_.lo (): %8x;\n")
300 ACE_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
301 static_cast<ACE_UINT32
> (start_
>> 32),
302 static_cast<ACE_UINT32
> (start_
& 0xfffffffful
),
303 static_cast<ACE_UINT32
> (end_
>> 32),
304 static_cast<ACE_UINT32
> (end_
& 0xfffffffful
),
305 static_cast<ACE_UINT32
> (total_
>> 32),
306 static_cast<ACE_UINT32
> (total_
& 0xfffffffful
),
307 static_cast<ACE_UINT32
> (start_incr_
>> 32),
308 static_cast<ACE_UINT32
> (start_incr_
& 0xfffffffful
)));
309 ACELIB_DEBUG ((LM_DEBUG
, ACE_END_DUMP
));
310 #endif /* ACE_HAS_DUMP */
314 ACE_High_Res_Timer::reset ()
316 ACE_TRACE ("ACE_High_Res_Timer::reset");
321 this->start_incr_
= 0;
325 ACE_High_Res_Timer::elapsed_time (ACE_Time_Value
&tv
) const
327 this->hrtime_to_tv (tv
,
328 ACE_High_Res_Timer::elapsed_hrtime (this->end_
, this->start_
));
331 #if defined (ACE_HAS_POSIX_TIME)
332 // Note... Win32 does not have ACE_HAS_POSIX_TIME, so the scale factor
333 // does not need to take into account the different units on Win32.
336 ACE_High_Res_Timer::elapsed_time (struct timespec
&elapsed_time
) const
338 // This implementation should be cleaned up.
340 // Just grab the nanoseconds. That is, leave off all values above
341 // microsecond. This equation is right! Don't mess with me! (It
342 // first strips off everything but the portion less than 1 usec.
343 // Then it converts that to nanoseconds by dividing by the scale
344 // factor to convert to usec, and multiplying by 1000.) The cast
345 // avoids a MSVC 4.1 compiler warning about narrowing.
346 ACE_hrtime_t elapsed
=
347 ACE_High_Res_Timer::elapsed_hrtime (this->end_
, this->start_
);
348 u_long nseconds
= static_cast<u_long
> (elapsed
%
349 global_scale_factor () * 1000u /
350 global_scale_factor ());
352 // Get just the microseconds (dropping any left over nanoseconds).
353 ACE_UINT32 useconds
= (ACE_UINT32
) (elapsed
/ global_scale_factor ());
355 elapsed_time
.tv_sec
= (time_t) (useconds
/ ACE_ONE_SECOND_IN_USECS
);
356 // Transforms one second in microseconds into nanoseconds.
357 elapsed_time
.tv_nsec
= (time_t) ((useconds
% ACE_ONE_SECOND_IN_USECS
) * 1000u + nseconds
);
359 #endif /* ACE_HAS_POSIX_TIME */
362 ACE_High_Res_Timer::elapsed_time_incr (ACE_Time_Value
&tv
) const
364 this->hrtime_to_tv (tv
, total_
);
368 ACE_High_Res_Timer::elapsed_time (ACE_hrtime_t
&nanoseconds
) const
370 #if !defined (ACE_WIN32)
371 // Please do _not_ rearrange this equation. It is carefully
372 // designed and tested to avoid overflow on machines that don't have
373 // native 64-bit ints. In particular, division can be a problem.
374 // For more background on this, please see bugzilla #1024.
375 nanoseconds
= ACE_High_Res_Timer::elapsed_hrtime (this->end_
, this->start_
)
376 * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
378 // Right shift is implemented for non native 64-bit ints
379 // operator/ only for a 32 bit result !
381 // This a higher-precision version, specific for Windows systems
383 (ACE_High_Res_Timer::elapsed_hrtime (this->end_
, this->start_
) * ACE_HR_SCALE_CONVERSION
* 1000u) /
384 ACE_High_Res_Timer::global_scale_factor ();
389 ACE_High_Res_Timer::elapsed_time_incr (ACE_hrtime_t
&nanoseconds
) const
391 #if !defined (ACE_WIN32)
393 nanoseconds
= this->total_
* (1024000u / ACE_High_Res_Timer::global_scale_factor ());
396 // This a higher-precision version, specific for Windows systems
397 nanoseconds
= this->total_
* 1000000000u / ACE_High_Res_Timer::global_scale_factor ();
402 ACE_High_Res_Timer::print_ave (const ACE_TCHAR
*str
,
404 ACE_HANDLE handle
) const
406 ACE_TRACE ("ACE_High_Res_Timer::print_ave");
408 // Get the total number of nanoseconds elapsed.
409 ACE_hrtime_t total_nanoseconds
;
410 this->elapsed_time (total_nanoseconds
);
412 // Separate to seconds and nanoseconds.
414 static_cast<u_long
> (total_nanoseconds
/ (ACE_UINT32
) ACE_ONE_SECOND_IN_NSECS
);
415 ACE_UINT32 extra_nsecs
=
416 static_cast<ACE_UINT32
> (total_nanoseconds
% (ACE_UINT32
) ACE_ONE_SECOND_IN_NSECS
);
421 ACE_hrtime_t avg_nsecs
= total_nanoseconds
/ (ACE_UINT32
) count
;
422 ACE_OS::snprintf (buf
, 100,
423 ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
426 (extra_nsecs
+ 500u) / 1000u,
427 (u_long
) ((avg_nsecs
+ 500u) / 1000u));
430 ACE_OS::snprintf (buf
, 100,
431 ACE_TEXT (" total %3lu.%06lu secs\n"),
433 (extra_nsecs
+ 500lu) / 1000lu);
435 ACE_OS::write (handle
,
437 ACE_OS::strlen (str
));
438 ACE_OS::write (handle
,
440 ACE_OS::strlen (buf
));
444 ACE_High_Res_Timer::print_total (const ACE_TCHAR
*str
,
446 ACE_HANDLE handle
) const
448 ACE_TRACE ("ACE_High_Res_Timer::print_total");
450 // Get the total number of nanoseconds elapsed.
451 ACE_hrtime_t total_nanoseconds
;
452 this->elapsed_time (total_nanoseconds
);
454 // Separate to seconds and nanoseconds.
456 static_cast<u_long
> (total_nanoseconds
/ (ACE_UINT32
) ACE_ONE_SECOND_IN_NSECS
);
457 ACE_UINT32 extra_nsecs
=
458 static_cast<ACE_UINT32
> (total_nanoseconds
% (ACE_UINT32
) ACE_ONE_SECOND_IN_NSECS
);
463 ACE_hrtime_t avg_nsecs
= this->total_
/ (ACE_UINT32
) count
;
465 ACE_OS::snprintf (buf
, 100,
466 ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
469 (extra_nsecs
+ 500u) / 1000u,
470 (u_long
) ((avg_nsecs
+ 500u) / 1000u));
473 ACE_OS::snprintf (buf
, 100,
474 ACE_TEXT (" total %3lu.%06u secs\n"),
476 (extra_nsecs
+ 500u) / 1000u);
478 ACE_OS::write (handle
,
480 ACE_OS::strlen (str
));
481 ACE_OS::write (handle
,
483 ACE_OS::strlen (buf
));
487 ACE_High_Res_Timer::get_env_global_scale_factor (const ACE_TCHAR
*env
)
491 const char *env_value
= ACE_OS::getenv (ACE_TEXT_ALWAYS_CHAR (env
));
494 int const value
= ACE_OS::atoi (env_value
);
497 ACE_High_Res_Timer::global_scale_factor (value
);
506 ACE_END_VERSIONED_NAMESPACE_DECL