[PATCH] glib/gtimer: nsleep on AIX, high resolution funcs



This patch provides two things:

1) Configure-time nsleep detection so that the same nanosleep functionality 
currently found in g_usleep will be used on AIX. The nsleep function is 
essentially the same as nanosleep and has the same semantics, AIX just renamed 
it.

2) Modification of g_timer internals to use nanosecond high resolution timers on
 AIX or platforms that have a gethrtime() function. this is currently Solaris, 
HP-UX, and RT-Linux implementations (possibly other flavors of UNIX as well). 
There are no API changes, but the GTimer structure size contains different types
 depending on the platform. (The size might not necessarily change, but that is 
up to the platform.)

This is useful when wanting to instrument cross-platform production code based 
upon glib with g_timer to gather function execution time data. Running the code 
through Quantify shows that the high resolution timer functions take ~3 cycles. 
Compared to gettimeofday(), they are about 10x faster.

If no one has objections, I ask that this be checked into glib CVS.

Thanks,

Andrew Paprocki
Bloomberg LP

--- configure.in        18 Nov 2005 14:01:59 -0000      1.474
+++ configure.in        20 Nov 2005 20:32:51 -0000
@@ -835,7 +835,10 @@
 AC_MSG_RESULT(unsigned $glib_size_type)

 # Check for some functions
-AC_CHECK_FUNCS(lstat strerror strsignal memmove mkstemp vsnprintf stpcpy 
strcasecmp strncasecmp poll getcwd nanosleep vasprintf setenv unsetenv 
getc_unlocked readlink symlink)
+AC_CHECK_FUNCS(lstat strerror strsignal memmove mkstemp vsnprintf stpcpy 
strcasecmp strncasecmp poll getcwd vasprintf setenv unsetenv getc_unlocked 
readlink symlink)
+
+# Check for high resolution sleep/time functions
+AC_CHECK_FUNCS(nanosleep nsleep gethrtime)

 AC_CHECK_HEADERS(crt_externs.h)
 AC_CHECK_FUNCS(_NSGetEnviron)
--- glib/gtimer.h       22 Jan 2004 19:56:26 -0000      1.5
+++ glib/gtimer.h       20 Nov 2005 20:32:51 -0000
@@ -38,6 +38,7 @@
 typedef struct _GTimer         GTimer;

 #define G_USEC_PER_SEC 1000000
+#define G_NSEC_PER_SEC 1000000000

 GTimer* g_timer_new    (void);
 void   g_timer_destroy (GTimer   *timer);
--- glib/gtimer.c       20 Mar 2005 11:35:48 -0000      1.26
+++ glib/gtimer.c       20 Nov 2005 20:32:51 -0000
@@ -35,17 +35,21 @@
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */

-#ifndef G_OS_WIN32
+#ifdef G_OS_WIN32
+# include <windows.h>
+#else /* !G_OS_WIN32 */
 #include <sys/time.h>
+# if defined(_AIX) && _AIX
+#  include <sys/systemcfg.h>
+# endif
 #include <time.h>
 #include <errno.h>
-#endif /* G_OS_WIN32 */
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif /* G_OS_WIN32 */
+#endif /* !G_OS_WIN32 */

-#include "glib.h"
+#include "gtypes.h"
+#include "gmessages.h"
+#include "gmem.h"
+#include "gtimer.h"
 #include "galias.h"


@@ -55,20 +59,52 @@
   guint64 start;
   guint64 end;
 #else /* !G_OS_WIN32 */
+# if defined(HAVE_GETHRTIME)
+  /* on platforms that support gethrtime(), hrtime_t is a 64-bit
+   * integer representing a number of nanoseconds since some point
+   * of time in the past.
+   *
+   * solaris, hpux, and rt-linux implementations are known to have
+   * this function.
+   */
+  hrtime_t start;
+  hrtime_t end;
+# elif defined(_AIX) && _AIX
+  /* on AIX systems with read_real_time(), the system calls use a
+   * hardware feature to read nanosecond resolution time from special
+   * registers on the cpu.
+   */
+  timebasestruct_t start;
+  timebasestruct_t end;
+# else /* !HAVE_GETHRTIME && !_AIX */
+  /* all other systems default to gettimeofday */
   struct timeval start;
   struct timeval end;
+# endif /* !HAVE_GETHRTIME && !_AIX */
 #endif /* !G_OS_WIN32 */

   guint active : 1;
 };

+static void
+g_timer_get_time(GTimer *timer, gboolean start)
+{
 #ifdef G_OS_WIN32
-#  define GETTIME(v) \
-     GetSystemTimeAsFileTime ((FILETIME *)&v)
+  GetSystemTimeAsFileTime((FILETIME *)(start ? timer->start : timer->end));
 #else /* !G_OS_WIN32 */
-#  define GETTIME(v) \
-     gettimeofday (&v, NULL)
+# if defined(HAVE_GETHRTIME)
+    if (start)
+      timer->start = gethrtime();
+    else
+      timer->end = gethrtime();
+# elif defined(_AIX) && _AIX
+  read_real_time(start ? &(timer->start) : &(timer->end), TIMEBASE_SZ);
+  time_base_to_time(start ? &(timer->start) : &(timer->end), TIMEBASE_SZ);
+# else /* !HAVE_GETHRTIME && !_AIX */
+  gettimeofday(start ? &(timer->start) : &(timer->end), NULL);
+# endif /* !HAVE_GETHRTIME && !_AIX */
 #endif /* !G_OS_WIN32 */
+}

 GTimer*
 g_timer_new (void)
@@ -78,7 +114,7 @@
   timer = g_new (GTimer, 1);
   timer->active = TRUE;

-  GETTIME (timer->start);
+  g_timer_get_time (timer, TRUE);

   return timer;
 }
@@ -98,7 +134,7 @@

   timer->active = TRUE;

-  GETTIME (timer->start);
+  g_timer_get_time (timer, TRUE);
 }

 void
@@ -108,7 +144,7 @@

   timer->active = FALSE;

-  GETTIME(timer->end);
+  g_timer_get_time (timer, FALSE);
 }

 void
@@ -116,7 +152,7 @@
 {
   g_return_if_fail (timer != NULL);

-  GETTIME (timer->start);
+  g_timer_get_time (timer, TRUE);
 }

 void
@@ -124,9 +160,15 @@
 {
 #ifdef G_OS_WIN32
   guint64 elapsed;
-#else
+#else /* !G_OS_WIN32 */
+# if defined(HAVE_GETHRTIME)
+  hrtime_t elapsed;
+# elif defined(_AIX) && _AIX
+  timebasestruct_t elapsed;
+# else /* !HAVE_GETHRTIME && !_AIX */
   struct timeval elapsed;
-#endif /* G_OS_WIN32 */
+# endif /* !HAVE_GETHRTIME && !_AIX */
+#endif /* !G_OS_WIN32 */

   g_return_if_fail (timer != NULL);
   g_return_if_fail (timer->active == FALSE);
@@ -137,17 +179,39 @@
    */

 #ifdef G_OS_WIN32
+  elapsed = timer->end - timer->start;
+
+  g_timer_get_time (timer, TRUE);

+  timer->start -= elapsed;
+#else /* !G_OS_WIN32 */
+# if defined(HAVE_GETHRTIME)
   elapsed = timer->end - timer->start;

-  GETTIME (timer->start);
+  g_timer_get_time (timer, TRUE);

   timer->start -= elapsed;
+# elif defined(_AIX) && _AIX
+  /* tb_low is a nanosecond count, tb_high is a second count */
+  if (timer->start.tb_low > timer->end.tb_low) {
+    timer->end.tb_low += G_NSEC_PER_SEC;
+    timer->end.tb_high--;
+  }

-#else /* !G_OS_WIN32 */
+  elapsed.tb_low = timer->end.tb_low - timer->start.tb_low;
+  elapsed.tb_high = timer->end.tb_high - timer->start.tb_high;

-  if (timer->start.tv_usec > timer->end.tv_usec)
-    {
+  g_timer_get_time (timer, TRUE);
+
+  if (timer->start.tb_low < elapsed.tb_low) {
+    timer->start.tb_low += G_NSEC_PER_SEC;
+    timer->start.tb_high--;
+  }
+
+  timer->start.tb_low -= elapsed.tb_low;
+  timer->end.tb_high -= elapsed.tb_high;
+# else /* !HAVE_GETHRTIME && !_AIX */
+  if (timer->start.tv_usec > timer->end.tv_usec) {
       timer->end.tv_usec += G_USEC_PER_SEC;
       timer->end.tv_sec--;
     }
@@ -155,17 +219,16 @@
   elapsed.tv_usec = timer->end.tv_usec - timer->start.tv_usec;
   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;

-  GETTIME (timer->start);
+  g_timer_get_time (timer, TRUE);

-  if (timer->start.tv_usec < elapsed.tv_usec)
-    {
+  if (timer->start.tv_usec < elapsed.tv_usec) {
       timer->start.tv_usec += G_USEC_PER_SEC;
       timer->start.tv_sec--;
     }

   timer->start.tv_usec -= elapsed.tv_usec;
   timer->start.tv_sec -= elapsed.tv_sec;
-
+# endif /* !HAVE_GETHRTIME && !_AIX */
 #endif /* !G_OS_WIN32 */

   timer->active = TRUE;
@@ -176,30 +239,61 @@
                 gulong *microseconds)
 {
   gdouble total;
+
 #ifdef G_OS_WIN32
   gint64 elapsed;
-#else
+#else /* !G_OS_WIN32 */
+# if defined(HAVE_GETHRTIME)
+  hrtime_t elapsed;
+# elif defined(_AIX) && _AIX
+  timebasestruct_t elapsed;
+# else /* !HAVE_GETHRTIME && !_AIX */
   struct timeval elapsed;
-#endif /* G_OS_WIN32 */
+# endif /* !HAVE_GETHRTIME && !_AIX */
+#endif /* !G_OS_WIN32 */

   g_return_val_if_fail (timer != NULL, 0);

-#ifdef G_OS_WIN32
   if (timer->active)
-    GETTIME (timer->end);
+    g_timer_get_time (timer, FALSE);

+#ifdef G_OS_WIN32
   elapsed = timer->end - timer->start;

+  /* elapsed is 100s of nanoseconds since 1/1/1601 */
   total = elapsed / 1e7;

   if (microseconds)
     *microseconds = (elapsed / 10) % 1000000;
 #else /* !G_OS_WIN32 */
-  if (timer->active)
-    gettimeofday (&timer->end, NULL);
+# if defined(HAVE_GETHRTIME)
+  elapsed = timer->end - timer->start;

-  if (timer->start.tv_usec > timer->end.tv_usec)
-    {
+  /* elapsed is a number of nanoseconds */
+  total = elapsed / 1e9;
+
+  if (microseconds)
+    *microseconds = (guint64)(elapsed / 1e3) % G_USEC_PER_SEC;
+# elif defined(_AIX) && _AIX
+  /* tb_low is a nanosecond count, tb_high is a second count */
+  if (timer->start.tb_low > timer->end.tb_low) {
+    timer->end.tb_low += G_NSEC_PER_SEC;
+    timer->end.tb_high--;
+  }
+
+  elapsed.tb_low = timer->end.tb_low - timer->start.tb_low;
+  elapsed.tb_high = timer->end.tb_high - timer->start.tb_high;
+
+  total = elapsed.tb_high + ((gdouble)elapsed.tb_low / 1e9);
+  if (total < 0) {
+    total = 0;
+    if (microseconds)
+      *microseconds = 0;
+  }
+  else if (microseconds)
+    *microseconds = elapsed.tb_low / 1000;
+# else /* !HAVE_GETHRTIME && !_AIX */
+  if (timer->start.tv_usec > timer->end.tv_usec) {
       timer->end.tv_usec += G_USEC_PER_SEC;
       timer->end.tv_sec--;
     }
@@ -208,16 +302,14 @@
   elapsed.tv_sec = timer->end.tv_sec - timer->start.tv_sec;

   total = elapsed.tv_sec + ((gdouble) elapsed.tv_usec / 1e6);
-  if (total < 0)
-    {
+  if (total < 0) {
       total = 0;
-
       if (microseconds)
        *microseconds = 0;
     }
   else if (microseconds)
     *microseconds = elapsed.tv_usec;
-
+# endif /* !HAVE_GETHRTIME && !_AIX */
 #endif /* !G_OS_WIN32 */

   return total;
@@ -229,13 +321,21 @@
 #ifdef G_OS_WIN32
   Sleep (microseconds / 1000);
 #else /* !G_OS_WIN32 */
-# ifdef HAVE_NANOSLEEP
+# if defined(HAVE_NANOSLEEP)
   struct timespec request, remaining;
   request.tv_sec = microseconds / G_USEC_PER_SEC;
   request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
   while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
     request = remaining;
-# else /* !HAVE_NANOSLEEP */
+# elif defined(HAVE_NSLEEP)
+  /* on AIX systems, nsleep is analogous to nanosleep */
+  struct timestruc_t request, remaining;
+  request.tv_sec = microseconds / G_USEC_PER_SEC;
+  request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
+  while (nsleep (&request, &remaining) == -1 && errno == EINTR)
+    request = remaining;
+# else /* !HAVE_NANOSLEEP && !HAVE_NSLEEP */
+# ifdef G_THREADS_ENABLED
   if (g_thread_supported ())
     {
       static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
@@ -263,11 +363,14 @@
     }
   else
     {
+# endif
       struct timeval tv;
       tv.tv_sec = microseconds / G_USEC_PER_SEC;
       tv.tv_usec = microseconds % G_USEC_PER_SEC;
       select(0, NULL, NULL, NULL, &tv);
+# ifdef G_THREADS_ENABLED
     }
+# endif /* G_THREADS_ENABLED */
 # endif /* !HAVE_NANOSLEEP */
 #endif /* !G_OS_WIN32 */
 }





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]