High_Res_Timer.cpp

Go to the documentation of this file.
00001 // $Id: High_Res_Timer.cpp 79134 2007-07-31 18:23:50Z johnnyw $
00002 
00003 // Be very carefull before changing the calculations inside
00004 // ACE_High_Res_Timer.  The precision matters and we are using integer
00005 // calculations not floating point.  Also look good at the emulated 64
00006 // bit int class (inside Basic_Types{h,i,cpp} before changing
00007 // anything.  It's operator/ only returns 32 bits not 64 bits, among
00008 // other things.
00009 
00010 #include "ace/High_Res_Timer.h"
00011 
00012 #if !defined (__ACE_INLINE__)
00013 #include "ace/High_Res_Timer.inl"
00014 #endif /* __ACE_INLINE__ */
00015 
00016 #include "ace/Stats.h"
00017 #include "ace/OS_NS_stdio.h"
00018 #include "ace/OS_NS_string.h"
00019 #include "ace/OS_NS_sys_time.h"
00020 #include "ace/OS_NS_time.h"
00021 #include "ace/OS_NS_unistd.h"
00022 #include "ace/OS_NS_stdlib.h"
00023 #include "ace/Truncate.h"
00024 
00025 ACE_RCSID(ace, High_Res_Timer, "$Id: High_Res_Timer.cpp 79134 2007-07-31 18:23:50Z johnnyw $")
00026 
00027 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00028 
00029 ACE_ALLOC_HOOK_DEFINE(ACE_High_Res_Timer)
00030 
00031 ACE_END_VERSIONED_NAMESPACE_DECL
00032 
00033 // For Intel platforms, a scale factor is required for
00034 // ACE_OS::gethrtime.  We'll still set this to one to prevent division
00035 // by zero errors.
00036 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
00037      defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
00038     !defined (ACE_HAS_HI_RES_TIMER)
00039 
00040 # include "ace/Guard_T.h"
00041 # include "ace/Recursive_Thread_Mutex.h"
00042 # include "ace/Object_Manager.h"
00043 
00044 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00045 
00046   // Initialize the global_scale_factor_ to 1.  The first
00047   // ACE_High_Res_Timer instance construction will override this
00048   // value.
00049   /* static */
00050   ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1u;
00051 
00052 ACE_END_VERSIONED_NAMESPACE_DECL
00053 
00054 #else  /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
00055              ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER)  ||
00056           ACE_HAS_HI_RES_TIMER */
00057 
00058 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00059 
00060   // A scale_factor of 1000 converts nanosecond ticks to microseconds.
00061   // That is, on these platforms, 1 tick == 1 nanosecond.
00062   /* static */
00063   ACE_UINT32 ACE_High_Res_Timer::global_scale_factor_ = 1000u;
00064 
00065 ACE_END_VERSIONED_NAMESPACE_DECL
00066 #endif /* ! (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
00067              ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER)  ||
00068           ACE_HAS_HI_RES_TIMER */
00069 
00070 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00071 
00072 // This is used to tell if the global_scale_factor_ has been
00073 // set, and if high resolution timers are supported.
00074 /* static */
00075 int ACE_High_Res_Timer::global_scale_factor_status_ = 0;
00076 
00077 
00078 #if defined (linux)
00079 // Determine the apparent CPU clock speed from /proc/cpuinfo
00080 ACE_UINT32
00081 ACE_High_Res_Timer::get_cpuinfo (void)
00082 {
00083   ACE_UINT32 scale_factor = 1u;
00084 
00085   // Get the BogoMIPS from /proc/cpuinfo.  It works fine on Alpha and
00086   // Pentium Pro.  For other CPUs, it will be necessary to interpret
00087   // the BogoMips, as described in the BogoMips mini-HOWTO.  Note that
00088   // this code assumes an order to the /proc/cpuinfo contents.  The
00089   // BogoMips rating had better come after CPU type and model info.
00090 #if !defined (__alpha__)
00091   int supported = 0;
00092 #endif /* __alpha__ */
00093 
00094   FILE *cpuinfo = ACE_OS::fopen (ACE_TEXT ("/proc/cpuinfo"),
00095                                  ACE_TEXT ("r"));
00096 
00097   if (cpuinfo != 0)
00098     {
00099       char buf[128];
00100 
00101       // ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nReading /proc/cpuinfo...")));
00102 
00103       while (ACE_OS::fgets (buf, sizeof buf, cpuinfo))
00104         {
00105 #if defined (__alpha__)
00106           ACE_UINT32 whole;
00107           ACE_UINT32 fractional;
00108           if (::sscanf (buf,
00109                         "BogoMIPS : %d.%d\n",
00110                         &whole,
00111                         &fractional) == 2
00112               || ::sscanf (buf,
00113                            "bogomips : %d.%d\n",
00114                            &whole,
00115                            &fractional) == 2)
00116             {
00117               scale_factor = whole;
00118               break;
00119             }
00120 #else
00121           double mhertz = 1;
00122           double bmips = 1;
00123           char arg[128];
00124 
00125           // CPU type?
00126           if (::sscanf (buf, "cpu : %s\n", arg) == 1)
00127             {
00128               // If this is an Alpha chip, then the BogoMips rating is
00129               // usable...
00130               if (ACE_OS::strncmp (arg,
00131                                    "Alpha",
00132                                    5) == 0)
00133                 {
00134                   supported = 1;
00135                   // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" recognized Alpha chip...")));
00136                 }
00137             }
00138           // Pentium CPU model?
00139           else if (supported == 0
00140                    && ::sscanf (buf, "model name : Pentium %s\n", arg) == 1)
00141             {
00142               // But if we don't have the right kind of Intel chip,
00143               // just quit.
00144               if (ACE_OS::strcmp (arg, "II") == 0
00145                   || ACE_OS::strcmp (arg, "III") == 0
00146                   || ACE_OS::strcmp (arg, "IV") == 0
00147                   || ACE_OS::strcmp (arg, "Pro") == 0)
00148                 {
00149                   supported = 1;
00150                   // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" recognized Pentium Pro/II chip...")));
00151                 }
00152             }
00153           else if (::sscanf (buf, "cpu MHz : %lf\n", &mhertz) == 1)
00154             {
00155               // If the line "cpu MHz : xxx" is present, then it's a
00156               // reliable measure of the CPU speed - according to the
00157               // kernel-source.
00158               scale_factor = (ACE_UINT32) (mhertz + 0.5);
00159               break;
00160             }
00161           else if (::sscanf (buf, "bogomips : %lf\n", &bmips) == 1
00162                    || ::sscanf (buf, "BogoMIPS : %lf\n", &bmips) == 1)
00163             {
00164               if (supported)
00165                 {
00166                   scale_factor = (ACE_UINT32) (bmips + 0.5);
00167                   // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" setting the clock scale factor to %u"), scale_factor));
00168                 }
00169 #if 0
00170               else
00171                 {
00172                   ACE_DEBUG ((LM_DEBUG,
00173                               ACE_TEXT ("\nThe BogoMIPS metric is not supported on this platform"
00174                                          "\n\tReport the results of the clock calibration and"
00175                                          "\n\tthe contents of /proc/cpuinfo to the ace-users mailing list")));
00176                 }
00177 #endif /* 0 */
00178               break;
00179             }
00180 #endif /* __alpha__ */
00181         }
00182 
00183       // ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (done)\n")));
00184 
00185       ACE_OS::fclose (cpuinfo);
00186     }
00187 
00188   return scale_factor;
00189 }
00190 #endif /* linux */
00191 
00192 ACE_UINT32
00193 ACE_High_Res_Timer::global_scale_factor (void)
00194 {
00195 #if (defined (ACE_WIN32) || defined (ACE_HAS_POWERPC_TIMER) || \
00196      defined (ACE_HAS_PENTIUM) || defined (ACE_HAS_ALPHA_TIMER)) && \
00197     !defined (ACE_HAS_HI_RES_TIMER) && \
00198     ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \
00199      defined (ghs) || defined (__GNUG__) || \
00200      defined (__INTEL_COMPILER))
00201   // Check if the global scale factor needs to be set, and do if so.
00202   if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
00203     {
00204       // Grab ACE's static object lock.  This doesn't have anything to
00205       // do with static objects; it's just a convenient lock to use.
00206       ACE_MT (ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon,
00207                                 *ACE_Static_Object_Lock::instance (), 0));
00208 
00209       // Double check
00210       if (ACE_High_Res_Timer::global_scale_factor_status_ == 0)
00211         {
00212 #         if defined (ACE_WIN32)
00213             LARGE_INTEGER freq;
00214             if (::QueryPerformanceFrequency (&freq))
00215               {
00216                 // We have a high-res timer
00217 #             if defined (ACE_LACKS_LONGLONG_T)
00218                 ACE_UINT64 uint64_freq(freq.u.LowPart, (ACE_UINT32) freq.u.HighPart);
00219                 ACE_High_Res_Timer::global_scale_factor
00220                   (uint64_freq / (ACE_UINT32) ACE_ONE_SECOND_IN_USECS);
00221 #             else
00222                 ACE_High_Res_Timer::global_scale_factor
00223                   (static_cast<unsigned int> (freq.QuadPart / ACE_HR_SCALE_CONVERSION));
00224 #             endif // (ACE_LACKS_LONGLONG_T)
00225 
00226                 ACE_High_Res_Timer::global_scale_factor_status_ = 1;
00227               }
00228             else
00229               // High-Res timers not supported
00230               ACE_High_Res_Timer::global_scale_factor_status_ = -1;
00231 
00232             return ACE_High_Res_Timer::global_scale_factor_;
00233 
00234 #         elif defined (linux)
00235             ACE_High_Res_Timer::global_scale_factor (ACE_High_Res_Timer::get_cpuinfo ());
00236 #         endif /* ! ACE_WIN32 && ! (linux && __alpha__) */
00237 
00238 #         if !defined (ACE_WIN32)
00239           if (ACE_High_Res_Timer::global_scale_factor_ == 1u)
00240             // Failed to retrieve CPU speed from system, so calculate it.
00241             ACE_High_Res_Timer::calibrate ();
00242 #         endif // (ACE_WIN32)
00243         }
00244     }
00245 
00246   ACE_High_Res_Timer::global_scale_factor_status_ = 1;
00247 #endif /* (ACE_WIN32 || ACE_HAS_POWERPC_TIMER || \
00248            ACE_HAS_PENTIUM || ACE_HAS_ALPHA_TIMER) && \
00249           ! ACE_HAS_HI_RES_TIMER &&
00250           ((WIN32 && ! WINCE) || ghs || __GNUG__) */
00251 
00252   return ACE_High_Res_Timer::global_scale_factor_;
00253 }
00254 
00255 ACE_High_Res_Timer::ACE_High_Res_Timer (void)
00256 {
00257   ACE_TRACE ("ACE_High_Res_Timer::ACE_High_Res_Timer");
00258 
00259   this->reset ();
00260 
00261   // Make sure that the global scale factor is set.
00262   (void) global_scale_factor ();
00263 }
00264 
00265 ACE_UINT32
00266 ACE_High_Res_Timer::calibrate (const ACE_UINT32 usec,
00267                                const u_int iterations)
00268 {
00269   const ACE_Time_Value sleep_time (0, usec);
00270   ACE_Stats delta_hrtime;
00271   // In units of 100 usec, to avoid overflow.
00272   ACE_Stats actual_sleeps;
00273 
00274   for (u_int i = 0;
00275        i < iterations;
00276        ++i)
00277     {
00278       const ACE_Time_Value actual_start =
00279         ACE_OS::gettimeofday ();
00280       const ACE_hrtime_t start =
00281         ACE_OS::gethrtime ();
00282       ACE_OS::sleep (sleep_time);
00283       const ACE_hrtime_t stop =
00284         ACE_OS::gethrtime ();
00285       const ACE_Time_Value actual_delta =
00286         ACE_OS::gettimeofday () - actual_start;
00287 
00288       // Store the sample.
00289       delta_hrtime.sample (ACE_Utils::truncate_cast<ACE_INT32> (stop - start));
00290       actual_sleeps.sample (actual_delta.msec () * 100u);
00291     }
00292 
00293   // Calculate the mean value of the samples, with no fractional
00294   // precision.  Use it for the global scale factor.
00295   ACE_Stats_Value ticks (0);
00296   delta_hrtime.mean (ticks);
00297 
00298   ACE_Stats_Value actual_sleep (0);
00299   actual_sleeps.mean (actual_sleep);
00300 
00301   // The addition of 5 below rounds instead of truncates.
00302   const ACE_UINT32 scale_factor =
00303     (ticks.whole () / actual_sleep.whole () + 5) /
00304     10u /* usec/100 usec */;
00305   ACE_High_Res_Timer::global_scale_factor (scale_factor);
00306 
00307   return scale_factor;
00308 }
00309 
00310 void
00311 ACE_High_Res_Timer::dump (void) const
00312 {
00313 #if defined (ACE_HAS_DUMP)
00314   ACE_TRACE ("ACE_High_Res_Timer::dump");
00315 
00316   ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
00317   ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\nglobal_scale_factor_: %u\n"),
00318              global_scale_factor ()));
00319 #if defined (ACE_LACKS_LONGLONG_T)
00320   ACE_DEBUG ((LM_DEBUG,
00321              ACE_TEXT (":\nstart_.hi ():     %8x; start_.lo ():      %8x;\n")
00322              ACE_TEXT ("end_.hi ():       %8x; end_.lo ():        %8x;\n")
00323              ACE_TEXT ("total_.hi ():     %8x; total_.lo ():      %8x;\n")
00324              ACE_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
00325              start_.hi (), start_.lo (),
00326              end_.hi (), end_.lo (),
00327              total_.hi (), total_.lo (),
00328              start_incr_.hi (), start_incr_.lo ()));
00329 #else  /* ! ACE_LACKS_LONGLONG_T */
00330   ACE_DEBUG ((LM_DEBUG,
00331               ACE_TEXT (":\nstart_.hi ():     %8x; start_.lo ():      %8x;\n")
00332               ACE_TEXT ("end_.hi ():       %8x; end_.lo ():        %8x;\n")
00333               ACE_TEXT ("total_.hi ():     %8x; total_.lo ():      %8x;\n")
00334               ACE_TEXT ("start_incr_.hi () %8x; start_incr_.lo (): %8x;\n"),
00335               static_cast<ACE_UINT32> (start_ >> 32),
00336               static_cast<ACE_UINT32> (start_ & 0xfffffffful),
00337               static_cast<ACE_UINT32> (end_ >> 32),
00338               static_cast<ACE_UINT32> (end_ & 0xfffffffful),
00339               static_cast<ACE_UINT32> (total_ >> 32),
00340               static_cast<ACE_UINT32> (total_ & 0xfffffffful),
00341               static_cast<ACE_UINT32> (start_incr_ >> 32),
00342               static_cast<ACE_UINT32> (start_incr_ & 0xfffffffful)));
00343 #endif /* ! ACE_LACKS_LONGLONG_T */
00344   ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP));
00345 #endif /* ACE_HAS_DUMP */
00346 }
00347 
00348 void
00349 ACE_High_Res_Timer::reset (void)
00350 {
00351   ACE_TRACE ("ACE_High_Res_Timer::reset");
00352 
00353   this->start_ = 0;
00354   this->end_ = 0;
00355   this->total_ = 0;
00356   this->start_incr_ = 0;
00357 }
00358 
00359 void
00360 ACE_High_Res_Timer::elapsed_time (ACE_Time_Value &tv) const
00361 {
00362   hrtime_to_tv (tv,
00363                 ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_));
00364 }
00365 
00366 #if defined (ACE_HAS_POSIX_TIME)
00367 // Note... Win32 does not have ACE_HAS_POSIX_TIME, so the scale factor
00368 // does not need to take into account the different units on Win32.
00369 
00370 void
00371 ACE_High_Res_Timer::elapsed_time (struct timespec &elapsed_time) const
00372 {
00373   // This implementation should be cleaned up.
00374 
00375   // Just grab the nanoseconds.  That is, leave off all values above
00376   // microsecond.  This equation is right!  Don't mess with me!  (It
00377   // first strips off everything but the portion less than 1 usec.
00378   // Then it converts that to nanoseconds by dividing by the scale
00379   // factor to convert to usec, and multiplying by 1000.)  The cast
00380   // avoids a MSVC 4.1 compiler warning about narrowing.
00381   ACE_hrtime_t elapsed =
00382     ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_);
00383   u_long nseconds = static_cast<u_long> (elapsed %
00384                                          global_scale_factor () * 1000u /
00385                                          global_scale_factor ());
00386 
00387   // Get just the microseconds (dropping any left over nanoseconds).
00388   ACE_UINT32 useconds = (ACE_UINT32) (elapsed / global_scale_factor ());
00389 
00390   elapsed_time.tv_sec = (time_t) (useconds / ACE_ONE_SECOND_IN_USECS);
00391   // Transforms one second in microseconds into nanoseconds.
00392   elapsed_time.tv_nsec = (time_t) ((useconds % ACE_ONE_SECOND_IN_USECS) * 1000u + nseconds);
00393 }
00394 #endif /* ACE_HAS_POSIX_TIME */
00395 
00396 void
00397 ACE_High_Res_Timer::elapsed_time_incr (ACE_Time_Value &tv) const
00398 {
00399   hrtime_to_tv (tv, total_);
00400 }
00401 
00402 void
00403 ACE_High_Res_Timer::elapsed_time (ACE_hrtime_t &nanoseconds) const
00404 {
00405   // Please do _not_ rearrange this equation.  It is carefully
00406   // designed and tested to avoid overflow on machines that don't have
00407   // native 64-bit ints. In particular, division can be a problem.
00408   // For more background on this, please see bugzilla #1024.
00409   nanoseconds = ACE_High_Res_Timer::elapsed_hrtime (this->end_, this->start_)
00410             * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
00411   // Caution - Borland has a problem with >>=, so resist the temptation.
00412   nanoseconds = nanoseconds >> 10;
00413   // Right shift is implemented for non native 64-bit ints
00414   // operator/ only for a 32 bit result !
00415 }
00416 
00417 void
00418 ACE_High_Res_Timer::elapsed_time_incr (ACE_hrtime_t &nanoseconds) const
00419 {
00420   // Same as above.
00421   nanoseconds = this->total_
00422             * (1024000u / ACE_High_Res_Timer::global_scale_factor ());
00423   // Caution - Borland has a problem with >>=, so resist the temptation.
00424   nanoseconds = nanoseconds >> 10;
00425 }
00426 
00427 #if !defined (ACE_HAS_WINCE)
00428 void
00429 ACE_High_Res_Timer::print_ave (const ACE_TCHAR *str,
00430                                const int count,
00431                                ACE_HANDLE handle) const
00432 {
00433   ACE_TRACE ("ACE_High_Res_Timer::print_ave");
00434 
00435   // Get the total number of nanoseconds elapsed.
00436   ACE_hrtime_t total_nanoseconds;
00437   this->elapsed_time (total_nanoseconds);
00438 
00439   // Separate to seconds and nanoseconds.
00440   u_long total_secs =
00441     static_cast<u_long> (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
00442   ACE_UINT32 extra_nsecs =
00443     static_cast<ACE_UINT32> (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
00444 
00445   ACE_TCHAR buf[100];
00446   if (count > 1)
00447     {
00448       ACE_hrtime_t avg_nsecs = total_nanoseconds / (ACE_UINT32) count;
00449       ACE_OS::sprintf (buf,
00450                        ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
00451                        count,
00452                        total_secs,
00453                        (extra_nsecs + 500u) / 1000u,
00454                        (u_long) ((avg_nsecs + 500u) / 1000u));
00455     }
00456   else
00457     ACE_OS::sprintf (buf,
00458                      ACE_TEXT (" total %3lu.%06lu secs\n"),
00459                      total_secs,
00460                      (extra_nsecs + 500lu) / 1000lu);
00461 
00462   ACE_OS::write (handle,
00463                  str,
00464                  ACE_OS::strlen (str));
00465   ACE_OS::write (handle,
00466                  buf,
00467                  ACE_OS::strlen (buf));
00468 }
00469 
00470 void
00471 ACE_High_Res_Timer::print_total (const ACE_TCHAR *str,
00472                                  const int count,
00473                                  ACE_HANDLE handle) const
00474 {
00475   ACE_TRACE ("ACE_High_Res_Timer::print_total");
00476 
00477   // Get the total number of nanoseconds elapsed.
00478   ACE_hrtime_t total_nanoseconds;
00479   this->elapsed_time (total_nanoseconds);
00480 
00481   // Separate to seconds and nanoseconds.
00482   u_long total_secs =
00483     (u_long) (total_nanoseconds / (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
00484   ACE_UINT32 extra_nsecs =
00485     (ACE_UINT32) (total_nanoseconds % (ACE_UINT32) ACE_ONE_SECOND_IN_NSECS);
00486 
00487   ACE_TCHAR buf[100];
00488   if (count > 1)
00489     {
00490       ACE_hrtime_t avg_nsecs = this->total_ / (ACE_UINT32) count;
00491 
00492       ACE_OS::sprintf (buf,
00493                        ACE_TEXT (" count = %d, total (secs %lu, usecs %u), avg usecs = %lu\n"),
00494                        count,
00495                        total_secs,
00496                        (extra_nsecs + 500u) / 1000u,
00497                        (u_long) ((avg_nsecs + 500u) / 1000u));
00498     }
00499   else
00500     ACE_OS::sprintf (buf,
00501                      ACE_TEXT (" total %3lu.%06u secs\n"),
00502                      total_secs,
00503                      (extra_nsecs + 500u) / 1000u);
00504 
00505   ACE_OS::write (handle,
00506                  str,
00507                  ACE_OS::strlen (str));
00508   ACE_OS::write (handle,
00509                  buf,
00510                  ACE_OS::strlen (buf));
00511 }
00512 #endif /* !ACE_HAS_WINCE */
00513 
00514 int
00515 ACE_High_Res_Timer::get_env_global_scale_factor (const ACE_TCHAR *env)
00516 {
00517 #if !defined (ACE_HAS_WINCE)
00518   if (env != 0)
00519     {
00520       const char *env_value = ACE_OS::getenv (ACE_TEXT_ALWAYS_CHAR (env));
00521       if (env_value != 0)
00522         {
00523           int const value = ACE_OS::atoi (env_value);
00524           if (value > 0)
00525             {
00526               ACE_High_Res_Timer::global_scale_factor (value);
00527               return 0;
00528             }
00529         }
00530     }
00531 #else
00532   ACE_UNUSED_ARG (env);
00533 #endif /* !ACE_HAS_WINCE */
00534   return -1;
00535 }
00536 
00537 ACE_END_VERSIONED_NAMESPACE_DECL

Generated on Sun Jan 27 12:05:28 2008 for ACE by doxygen 1.3.6