/*********************************************************************** Thread Timers Here are Isom's lightweight, thread-safe wall clock time and CPU time routines. They work for compiler based parallelism, cpslib, and MPI or regular processes. Some improvements for pthreads based parallelism are needed. Compile: cc -D_CONVEX_SOURCE -c timers.c Link: cc/f77 *.o -Wl,-aarchive_shared,+FPD -lail -lcnx_syscall ^^^^^^^^^^^^^^^^^^^^^^^^^ optional, but recommended *********************************************************************** * * cpu_time(ref) - Subroutine intended to be lightweight and * thread-safe mechanism to return individual thread * cpu time for the Convex SPP. It does not return * system (kernel or time blocked waiting) time, only * * cpu time spent executing user code. * ARGS: ref - address containing floating point (real*4) value * used to determine whether timer should be reset or * not. 0.0 forces the timer to be reset. * NOTE: *ref != 0.0 is not used by timers, it simply * indicates that the timer is not to be reset. * * Author: Isom Crawford, HP/Convex * * Date: 18 April 1996 * * Usage: Mainly intended for use on Convex SPP machines, although * non Convex compilers should compile without a problem * (and get wrappers for gettimeofdday() and clock()). * * FORTRAN: * * REAL*4 T0, T1, CPU_TIME * ... * * T0 = 0.0 * t0 = CPU_TIME( T0 ) * * ... Code segment to be timed. * * T1 = CPU_TIME( t0 ) * * C: * * float t0, t1, cpu_time; * ... * * t0 = 0.0; * t0 = cpu_time( &t0 ); * * ... Code segment to be timed * * t1 = cpu_time( &t0 ); * * * Warnings: When used with fork() and vfork() system calls, the * user should note the following: * * 1> If calls are made to cpu_time/wall_time before the * fork, then those routines should _NOT_ be called in * the child. Doing so may cause the child * process(es) to receive a SIGSEGV. The parent may * make calls before and after the fork without a * problem provided the child does not make calls to * cpu_time. * * 2> Calls to cpu_time() and wall_time() may be made by * both parent and child subsequent to a fork(). * * 3> SIGSEGV. The parent may make calls before and after * the fork without a problem provided the child does * not make calls to cpu_time. * * When called with *ref != 0.0, this simply indicates * that the timer is not reset - _THE_VALUE_IS_NOT_USED_! * This was done primarily to protect the user, i.e. to * guarantee that times are thread-private. * * These timers provide _THREAD_SPECIFIC_ info. Hence, * timing around thread spawns/joins will only return info * for thread 0, and _NOT_ all threads. Of course, timing * inside the parallel regions will return thread-unique * cpu_time/wall_time for each thread. * **********************************************************************/ #if !defined( _CONVEX_SOURCE ) # include #else # include # include # include #endif #define SECS_PER_MUSEC 0.000001 #define TWO_TO_32 4294967296.0 float cpu_time(ref) float *ref; { #if !defined( _CONVEX_SOURCE ) double t; clock_t clockret; if( ref == NULL ) { perror("cpu_time/NULL address"); exit(-1); } clockret= clock(); t= ((double)clockret) / ((double)CLOCKS_PER_SEC) - (double)(*ref); return (t); #else /*************************************************************** * ttr_per_sec := # of thread timer ticks per second. * thread_private in anticipation of mixed node * configurations. **************************************************************/ static thread_private double ttr_per_sec_recip; /*************************************************************** * handle := handle used by ttr_enable() and must be thread - * unique. **************************************************************/ static thread_private unsigned int handle; /*************************************************************** * flag[0-128] := Array of flags for each thread. That is, i-th * entry indicates whether ttr_enable() needs to * be called. Declared static as it must be * retained. **************************************************************/ static unsigned int flag[128]= { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; /*************************************************************** * OTHER VARIABLES: * ktid := This thread's id #. * temp_ttr := 64 bit counter temporary. * ptr := int pointer used to convert temp_ttr to integers * temp_secs := temporary used to convert ptr[] to floating * point. **************************************************************/ int ktid; cnx_ttr_t temp_ttr; unsigned int *ptr; double temp_secs; ptr= (unsigned int *)&temp_ttr; ktid= cps_ktid(); if( ktid < 0 ) { perror("cpu_time/ktid"); exit(-1); } if( ref == NULL ) { perror("cpu_time/NULL address"); exit(-1); } /************************************************************* * If flag[thread id] is not set, then timer must be enabled. ************************************************************/ if( flag[ktid] == 0 ) { /***************************************************** * Initialize thread timer ticks per second for this * thread. ****************************************************/ ttr_per_sec_recip= 1.0 / (double)ttr_per_sec_read(); if( ttr_per_sec_recip <= 0 ) { perror("ttr_per_sec_read() in cpu_time"); exit(-1); } /***************************************************** * Enable timer for this thread. ****************************************************/ if (ttr_enable(&handle) == -1) { perror("ttr_enable() in cpu_time"); exit(-1); } /***************************************************** * Mark flag so that thread timer won't be enabled * multiple times. ****************************************************/ flag[ktid]= 1; } /************************************************************* * If argument is 0, reset timer. ************************************************************/ if ( *ref == 0. ) { /***************************************************** * Set 64 bit counter to zero (via pointers). ****************************************************/ ptr[0]= ptr[1]= 0; /***************************************************** * Hammer timer. ****************************************************/ ttr_write(handle, temp_ttr); } /************************************************************* * end if ( *ref == 0.0 ) ************************************************************/ /************************************************************* * Get 64 bit counter from thread timer. Then convert to * double precision seconds. Deduct base seconds for correct * time since last initialization. ************************************************************/ temp_ttr= ttr_read(handle); temp_secs= ( (double)ptr[0] ) * TWO_TO_32 ; temp_secs+= ( (double)ptr[1] ) ; temp_secs= temp_secs * ttr_per_sec_recip ; return( (float)temp_secs ); #endif } /*********************************************************************** * * wall_time(ref) - Subroutine intended to be lightweight and * thread-safe mechanism to return individual thread * wall time for the Convex SPP. * * ARGS: ref - address containing floating point (real*4) value * used to determine whether timer should be reset or * not. 0.0 forces the timer to be reset. * * Author: Isom Crawford, HP/Convex * **********************************************************************/ float wall_time(ref) float *ref; { #if !defined( _CONVEX_SOURCE ) double t; static unsigned long base_sec= 0; struct timeval buffer; struct timezone dummy; gettimeofday (&buffer, &dummy); if( base_sec <= 0 ) base_sec= buffer.tv_sec; buffer.tv_sec -= base_sec; if( ref == NULL ) { perror("wall_time/NULL address"); exit(-1); } t = (double)buffer.tv_sec + ((double)buffer.tv_usec*1.0e-6) - (double)(*ref); return (t); #else /*************************************************************** * base_ptr0 := # of seconds at initialization time (*ref = 0). * This must be static and thread_private to * maintain retained, separate information for * each thread. **************************************************************/ static thread_private unsigned int base_ptr0; static thread_private double base_secs; /*************************************************************** * OTHER VARIABLES: * temp_toc := 64 bit counter temporary. * ptr := int pointer used to convert temp_toc to integers * temp_secs := temporary used to convert ptr[] to floating * point. **************************************************************/ cnx_toc_t temp_toc; unsigned int *ptr; double temp_secs; if( ref == NULL ) { perror("wall_time/NULL address"); exit(-1); } ptr= (unsigned int *)&temp_toc; /*************************************************************** * Get time of century. **************************************************************/ temp_toc= toc_read(); /*************************************************************** * If arg (*ref) is <= 0.0, reset baseline time. Make second * call to toc_read() to make absolutely sure that return value * is > 0.0. **************************************************************/ if ( *ref <= 0. ) { /* Reset base_ptr0 */ base_ptr0= ptr[0]; base_secs= ( (double)ptr[1] ) * SECS_PER_MUSEC ; temp_toc= toc_read(); } ptr[0] -= base_ptr0; /*************************************************************** * Convert 64 bit counter to double precision. **************************************************************/ temp_secs= ( (double)ptr[0] ) * TWO_TO_32 ; temp_secs+= ( (double)ptr[1] ) ; temp_secs*= SECS_PER_MUSEC ; temp_secs= temp_secs - base_secs - (double)(*ref); return( (float)temp_secs ); #endif }