Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2007 Pekka Riikonen
+ Copyright (C) 2007 - 2008 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
-/****h* silcutil/SILC Timer Interface
+/****h* silcutil/Timer Interface
*
* DESCRIPTION
*
* SILC Timer interface provides a simple way to measure time intervals.
* The SILC Timer works with microsecond resolution, depending on platform.
*
+ * The interface also provides utility functions to get currenet CPU tick
+ * count and to delay process/thread execution.
+ *
* EXAMPLE
*
* SilcTimerStruct timer;
#ifndef SILCTIMER_H
#define SILCTIMER_H
-/****s* silcutil/SilcTimerAPI/SilcTimer
+/****s* silcutil/SilcTimer
*
* NAME
*
***/
typedef struct SilcTimerObject *SilcTimer, SilcTimerStruct;
-/****f* silcutil/SilcTimerAPI/silc_timer_start
+/****f* silcutil/silc_timer_start
*
* SYNOPSIS
*
***/
void silc_timer_start(SilcTimer timer);
-/****f* silcutil/SilcTimerAPI/silc_timer_stop
+/****f* silcutil/silc_timer_stop
*
* SYNOPSIS
*
***/
void silc_timer_stop(SilcTimer timer);
-/****f* silcutil/SilcTimerAPI/silc_timer_continue
+/****f* silcutil/silc_timer_continue
*
* SYNOPSIS
*
***/
void silc_timer_continue(SilcTimer timer);
-/****f* silcutil/SilcTimerAPI/silc_timer_value
+/****f* silcutil/silc_timer_value
*
* SYNOPSIS
*
SilcUInt64 *elapsed_time_seconds,
SilcUInt32 *elapsed_time_microseconds);
-/****f* silcutil/SilcTimerAPI/silc_timer_value_time
+/****f* silcutil/silc_timer_value_time
*
* SYNOPSIS
*
***/
void silc_timer_value_time(SilcTimer timer, SilcTime ret_time);
-/****f* silcutil/SilcTimerAPI/silc_timer_start_time
+/****f* silcutil/silc_timer_start_time
*
* SYNOPSIS
*
***/
void silc_timer_start_time(SilcTimer timer, SilcTime ret_start_time);
-/****f* silcutil/SilcTimerAPI/silc_timer_is_running
+/****f* silcutil/silc_timer_is_running
*
* SYNOPSIS
*
#include "silctimer_i.h"
+/****f* silcutil/silc_timer_tick
+ *
+ * SYNOPSIS
+ *
+ * SilcUInt64 silc_timer_tick(SilcTimer &timer, SilcBool adjust)
+ *
+ * DESCRIPTION
+ *
+ * Returns the current CPU tick count. You should call the
+ * silc_timer_synchronize before using this function to make sure the
+ * overhead of measuring the CPU tick count is not included in the
+ * tick count. If the `adjust' is TRUE and the silc_timer_synchronize
+ * has been called the returned value is adjusted to be more accurate.
+ *
+ * EXAMPLE
+ *
+ * // Synchronize timer for more accurate CPU tick counts
+ * silc_timer_synchronize(&timer);
+ * start = silc_timer_tick(&timer, FALSE);
+ * do_something();
+ * stop = silc_timer_tick(&timer, TRUE);
+ *
+ ***/
+
+static inline
+SilcUInt64 silc_timer_tick(SilcTimer timer, SilcBool adjust)
+{
+#if defined(__GNUC__) || defined(__ICC)
+#if defined(SILC_I486)
+ SilcUInt64 x;
+ asm volatile ("rdtsc" : "=A" (x));
+ return adjust ? x - timer->sync_tdiff : x;
+
+#elif defined(SILC_X86_64)
+ SilcUInt64 x;
+ SilcUInt32 hi, lo;
+ asm volatile ("rdtsc" : "=a" (lo), "=d" (hi));
+ x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
+ return adjust ? x - timer->sync_tdiff : x;
+
+#elif defined(SILC_POWERPC)
+ SilcUInt32 hi, lo, tmp;
+ asm volatile ("0: \n\t"
+ "mftbu %0 \n\t"
+ "mftb %1 \n\t"
+ "mftbu %2 \n\t"
+ "cmpw %2,%0 \n\t"
+ "bne 0b \n"
+ : "=r" (hi), "=r" (lo), "=r" (tmp));
+ x = ((SilcUInt64)lo | ((SilcUInt64)hi << 32));
+ return adjust ? x - timer->sync_tdiff : x;
+#endif /* SILC_I486 */
+
+#elif defined(SILC_WIN32)
+ __asm rdtsc
+
+#else
+ return 0;
+#endif /* __GNUC__ || __ICC */
+}
+
+/****f* silcutil/silc_timer_synchronize
+ *
+ * SYNOPSIS
+ *
+ * void silc_timer_synchronize(SilcTimer timer);
+ *
+ * DESCRIPTION
+ *
+ * Synchronizes the `timer'. This call will attempt to synchronize the
+ * timer for more accurate results with high resolution timing. Call
+ * this before you start using the `timer'.
+ *
+ * EXAMPLE
+ *
+ * // Synchronize timer
+ * silc_timer_synchronize(&timer);
+ * silc_timer_start(&timer);
+ * ... time passes ...
+ * silc_timer_stop(&timer);
+ * silc_timer_value(&timer, &elapsed_sec, &elapsed_usec);
+ *
+ ***/
+
+static inline
+void silc_timer_synchronize(SilcTimer timer)
+{
+ SilcUInt32 tdiff, cumu, i;
+ SilcUInt64 t1, t2;
+
+ /* Sync normal timer */
+ for (i = 0, cumu = 0; i < 5; i++) {
+ silc_timer_start(timer);
+ silc_timer_stop(timer);
+ silc_timer_value(timer, NULL, &tdiff);
+ cumu += (int)tdiff;
+ }
+
+ timer->sync_diff = cumu;
+ if (timer->sync_diff > 5)
+ timer->sync_diff /= 5;
+
+ /* Sync CPU tick count */
+ cumu = 0;
+ t1 = silc_timer_tick(timer, FALSE);
+ t2 = silc_timer_tick(timer, FALSE);
+ cumu += (t2 - t1);
+ t1 = silc_timer_tick(timer, FALSE);
+ t2 = silc_timer_tick(timer, FALSE);
+ cumu += (t2 - t1);
+ t1 = silc_timer_tick(timer, FALSE);
+ t2 = silc_timer_tick(timer, FALSE);
+ cumu += (t2 - t1);
+
+ timer->sync_tdiff = cumu / 3;
+
+ t1 = silc_timer_tick(timer, FALSE);
+ t2 = silc_timer_tick(timer, TRUE);
+ timer->sync_tdiff += (int)(t2 - t1);
+}
+
+/****f* silcutil/silc_usleep
+ *
+ * SYNOPSIS
+ *
+ * void silc_usleep(unsigned long microseconds);
+ *
+ * DESCRIPTION
+ *
+ * Delays the execution of process/thread for the specified amount of
+ * time, which is in microseconds.
+ *
+ * NOTES
+ *
+ * The delay is only approximate and on some platforms the resolution is
+ * in fact milliseconds.
+ *
+ ***/
+static inline
+void silc_usleep(unsigned long microseconds)
+{
+#ifdef SILC_UNIX
+#ifdef HAVE_NANOSLEEP
+ struct timespec tv;
+ tv.tv_sec = 0;
+ tv.tv_nsec = microseconds * 1000;
+#endif /* HAVE_NANOSLEEP */
+#endif /* SILC_UNIX */
+
+#ifdef SILC_UNIX
+#ifdef HAVE_NANOSLEEP
+ nanosleep(&tv, NULL);
+#else
+ usleep(microseconds);
+#endif /* HAVE_NANOSLEEP */
+#endif /* SILC_UNIX */
+#ifdef SILC_WIN32
+ Sleep(microseconds / 1000);
+#endif /* SILC_WIN32 */
+#ifdef SILC_SYMBIAN
+ silc_symbian_usleep(microseconds);
+#endif /* SILC_SYMBIAN */
+}
+
#endif /* SILCTIMER_H */