File contents
/***********************************************************************
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 <time.h>
#else
# include <spp_prog_model.h>
# include <sys/types.h>
# include <sys/cnx_ail.h>
#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
}