/* A fast, potentially portable interface to a hardware clock. */ /* On Pentium and "greater", compile hwclock.c and #include "hwclock.h" with the pre-processor symbol __iX86__ set to 586 or greater. Then you'll get a very fast inlined timer running at the fundamental frequency of the cpu, e.g., 200Mhz, based on the 'rdtsc' instruction. In other cases, the same functions work, but they just call gettimeofday() and convert the result to double (which at least avoids overflow). API: typedef hwclock_t; void hwclock(hwclock_t *); double hwsec_per_clock(void); #define HWC_FMT hwclock_t is an arithmetic type, but may be either integer or floating. Expect either a 64-bit integer or a double. This makes it annoying to 'printf'. HWCFMT is a hack that at least makes it possible, but by no means pretty. hwsec_per_clock tells how to convert from hwclock_t's to seconds. It doesn't (necessarily) tell you anything about the granularity. TODO: hwgranularity() - try to tell the caller something about the resolution of the clock, c.f. MPI_Wtick(). */ #if defined( __GNUC__) && __iX86__>=586 typedef unsigned long long hwclock_t; #define HWC_FMT "lld" #else /* the generic case - just call gettimeofday and convert to double */ typedef double hwclock_t; #define HWC_FMT "f" #endif extern double hwsec_per_clock(void); extern void hwclock(hwclock_t *p); /****************INLINING AND OTHER SPECIAL HACKS ************************/ #if defined(__GNUC__) #if defined(hwCLOCKdotC) #define INLINE __inline__ #else #define INLINE extern __inline__ #endif #endif #if defined(__GNUC__) && __iX86__>=586 extern double _hwsec_per_clock; /* not for public use */ extern int _hwsec_per_clock_known; /* not for public use */ extern double _get_hwsec_per_clock(void); /* not for public use */ /* This function inlines to 3 instructions with gcc -O2. It takes about 33 ticks on noisy, a 200Mhz Ppro. */ INLINE void hwclock(hwclock_t *p){ unsigned long *plo, *phi; plo = (unsigned long *)p; phi = plo+1; asm volatile ("rdtsc\n\t" : "=a" (*plo), "=d" (*phi) : /* No input registers */ : "%eax", "%edx"); } INLINE double hwsec_per_clock(void){ if( _hwsec_per_clock_known ) return _hwsec_per_clock; else return _get_hwsec_per_clock(); } #endif /* defined(__GNUC__) && __iX86__>=586 */