root/ompi/tools/mpisync/hpctimer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hpctimer_initialize
  2. hpctimer_finalize
  3. hpctimer_print_timers
  4. hpctimer_sanity_check
  5. hpctimer_wtime
  6. hpctimer_wtime_gettimeofday
  7. hpctimer_wtime_tsc
  8. hpctimer_tsc_initialize
  9. hpctimer_gettsc
  10. hpctimer_measure_overhead
  11. hpctimer_calibrate_sleep

   1 /*
   2  * Copyright (c) 2010-2011, Siberian State University of Telecommunications
   3  *                          and Information Sciences. All rights reserved.
   4  * Copyright (c) 2010-2011, A.V. Rzhanov Institute of Semiconductor Physics SB RAS.
   5  *                          All rights reserved.
   6  *
   7  * hpctimer.c: High-Resolution timers library.
   8  * http://mpiperf.cpct.sibsutis.ru/index.php
   9  *
  10  * Copyright (C) 2011 Mikhail Kurnosov <mkurnosov@gmail.com>
  11  *
  12  * Redistribution and use in source and binary forms, with or without
  13  * modification, are permitted provided that the following conditions are met:
  14  * Redistributions of source code must retain the above copyright
  15  * notice, this list of conditions and the following disclaimer.
  16  *
  17  * Redistributions in binary form must reproduce the above copyright
  18  * notice, this list of conditions and the following disclaimer in the
  19  * documentation and/or other materials provided with the distribution.
  20  *
  21  * Neither the name of the the copyright holders nor the
  22  * names of its contributors may be used to endorse or promote products
  23  * derived from this software without specific prior written permission.
  24  *
  25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  27  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  28  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  29  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  32  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  35  *
  36  * $COPYRIGHT$
  37  *
  38  * Additional copyrights may follow
  39  *
  40  * $HEADER$
  41  */
  42 
  43 #include <sys/time.h>
  44 #include <unistd.h>
  45 
  46 #include <stdio.h>
  47 #include <stdlib.h>
  48 #include <strings.h>
  49 #include <math.h>
  50 #include <inttypes.h>
  51 
  52 #include <mpi.h>
  53 
  54 #include "hpctimer.h"
  55 
  56 #define NELEMS(v) (sizeof(v) / sizeof((v)[0]))
  57 
  58 /*
  59  * Compilers macro:
  60  * __GNUC__ - GCC
  61  * __SUNPRO_C - Solaris Studio
  62  * __INTEL_COMPILER - Intel C++ Compiler
  63  * __xlC__ || __IBMC__ - IBM C Compiler
  64  * __PATHSCALE__ - PathScale Compiler
  65  * __PGI - PGI Compiler
  66  * __DECC - DEC Compiler
  67  * __HP_cc - HP Compiler
  68  * __SX - NEC SX Compiler
  69  * __COMO__ - Comeau C++
  70  * _CRAYC - Cray C Compiler
  71  * sgi || __sgi - SGI Compiler
  72  */
  73 
  74 #if defined(__GNUC__)
  75 #   define __inline__ __inline__
  76 #   define __asm__ __asm__
  77 #   define __volatile__ __volatile__
  78 #elif defined(__SUNPRO_C)
  79 #   define __inline__ __inline__
  80 #   define __asm__ __asm__
  81 #   define __volatile__ __volatile__
  82 #endif
  83 
  84 typedef int (*hpctimer_initialize_func_ptr_t)(void);
  85 typedef void (*hpctimer_finalize_func_ptr_t)(void);
  86 typedef int (*hpctimer_isimplemented_func_ptr_t)(void);
  87 typedef double (*hpctimer_wtime_func_ptr_t)(void);
  88 
  89 typedef struct hpctimer {
  90     char *name;
  91     hpctimer_initialize_func_ptr_t initialize;
  92     hpctimer_finalize_func_ptr_t finalize;
  93     hpctimer_isimplemented_func_ptr_t isimplemented;
  94     hpctimer_wtime_func_ptr_t wtime;
  95 } hpctimer_t;
  96 
  97 static uint64_t hpctimer_overhead;  /* Timer overhead (seconds) */
  98 static uint64_t hpctimer_freq;      /* Timer frequency (ticks per usec) */
  99 
 100 static double hpctimer_wtime_tsc(void);
 101 static int hpctimer_tsc_initialize(void);
 102 static __inline__ uint64_t hpctimer_gettsc(void);
 103 static uint64_t hpctimer_measure_overhead(void);
 104 static uint64_t hpctimer_calibrate_sleep(uint64_t overhead);
 105 static double hpctimer_wtime_gettimeofday(void);
 106 
 107 /*
 108  * Timers
 109  */
 110 static hpctimer_t hpctimer_timers[] = {
 111     {"MPI_Wtime", NULL, NULL, NULL, MPI_Wtime},
 112     {"gettimeofday", NULL, NULL, NULL, hpctimer_wtime_gettimeofday},
 113     {"tsc", hpctimer_tsc_initialize, NULL, NULL, hpctimer_wtime_tsc}
 114 };
 115 
 116 static hpctimer_wtime_func_ptr_t hpctimer_wtime_func_ptr = NULL;
 117 static int hpctimer_timer = -1;
 118 
 119 /* hpctimer_initialize: */
 120 int hpctimer_initialize(const char *timername)
 121 {
 122     hpctimer_wtime_func_ptr = NULL;
 123     hpctimer_timer = -1;
 124     unsigned int i;
 125     for (i = 0; i < NELEMS(hpctimer_timers); i++) {
 126         if (hpctimer_timers[i].isimplemented != NULL) {
 127             if (!hpctimer_timers[i].isimplemented()) {
 128                 continue;
 129             }
 130         }
 131         if (strcasecmp(timername, hpctimer_timers[i].name) == 0) {
 132             hpctimer_wtime_func_ptr = hpctimer_timers[i].wtime;
 133             hpctimer_timer = i;
 134             if (hpctimer_timers[i].initialize) {
 135                 return hpctimer_timers[i].initialize();
 136             }
 137             return HPCTIMER_SUCCESS;
 138         }
 139     }
 140     return HPCTIMER_FAILURE;
 141 }
 142 
 143 /* hpctimer_finalize: */
 144 void hpctimer_finalize(void)
 145 {
 146     if (hpctimer_timers[hpctimer_timer].finalize) {
 147         hpctimer_timers[hpctimer_timer].finalize();
 148     }
 149     hpctimer_wtime_func_ptr = NULL;
 150 }
 151 
 152 /* hpctimer_print_timers: */
 153 void hpctimer_print_timers(void)
 154 {
 155     unsigned int i;
 156 
 157     printf("Supported timers:\n");
 158     for (i = 0; i < NELEMS(hpctimer_timers); i++) {
 159         if (hpctimer_timers[i].isimplemented != NULL) {
 160             if (!hpctimer_timers[i].isimplemented()) {
 161                 continue;
 162             }
 163         }
 164         printf("    %s\n", hpctimer_timers[i].name);
 165     }
 166 }
 167 
 168 /*
 169  * hpctimer_sanity_check: Returns 1 if the results of measures
 170  *                        by timer are correct.
 171  */
 172 int hpctimer_sanity_check(void)
 173 {
 174     enum { NTESTS = 4 };
 175     double start, stop, currtime, prevtime = 0.0, err = 0.05;
 176     int sanity = 1;
 177 
 178     int delay = 0;
 179     for (delay = 1; delay < NTESTS; delay++) {
 180         start = hpctimer_wtime();
 181         sleep(delay);
 182         stop = hpctimer_wtime();
 183         currtime = stop - start;
 184         if (delay > 1) {
 185             if (fabs(prevtime - currtime / delay) > prevtime * err) {
 186                 sanity = 0;
 187             }
 188             /*
 189             printf("# timer sleep %d sec.; timer result: %.6f; diff: %.6f\n",
 190                    delay - 1, currtime / delay, fabs(prevtime - currtime / delay));
 191             */
 192         }
 193         prevtime = currtime / delay;
 194     }
 195     return sanity;
 196 }
 197 
 198 /* hpctimer_wtime: Returns walltime in seconds. */
 199 double hpctimer_wtime(void)
 200 {
 201     return hpctimer_wtime_func_ptr();
 202 }
 203 
 204 /* hpctimer_wtime_gettimeofday: */
 205 static double hpctimer_wtime_gettimeofday(void)
 206 {
 207     struct timeval tv;
 208     gettimeofday(&tv, NULL);
 209     return (double)tv.tv_sec + 1E-6 * tv.tv_usec;
 210 }
 211 
 212 /*
 213  * hpctimer_wtime_tsc: Returns TSC-based walltime in seconds.
 214  */
 215 static double hpctimer_wtime_tsc(void)
 216 {
 217     return (double)(hpctimer_gettsc() - hpctimer_overhead) / (double)hpctimer_freq;
 218 }
 219 
 220 /*
 221  * hpctimer_tsc_initialize: Initializes TSC-based timer.
 222  *
 223  * The code is based on recommendations from manual of Intel Corp.
 224  * "Using the RDTSC Instruction for Performance Monitoring".
 225  */
 226 static int hpctimer_tsc_initialize(void)
 227 {
 228     hpctimer_overhead = hpctimer_measure_overhead();
 229     hpctimer_freq = hpctimer_calibrate_sleep(hpctimer_overhead);
 230     return HPCTIMER_SUCCESS;
 231 }
 232 
 233 /*
 234  * hpctimer_gettsc: Returns TSC value.
 235  */
 236 static __inline__ uint64_t hpctimer_gettsc(void)
 237 {
 238 #if defined(__x86_64__)
 239     uint32_t low, high;
 240     __asm__ __volatile__(
 241         "xorl %%eax, %%eax\n"
 242         "cpuid\n"
 243         ::: "%rax", "%rbx", "%rcx", "%rdx"
 244     );
 245     __asm__ __volatile__(
 246         "rdtsc\n"
 247         : "=a" (low), "=d" (high)
 248     );
 249     return ((uint64_t)high << 32) | low;
 250 
 251 #elif defined(__i386__)
 252     uint64_t tsc;
 253     __asm__ __volatile__(
 254         "xorl %%eax, %%eax\n"
 255         "cpuid\n"
 256         ::: "%eax", "%ebx", "%ecx", "%edx"
 257     );
 258     __asm__ __volatile__(
 259         "rdtsc\n"
 260         : "=A" (tsc)
 261     );
 262     return tsc;
 263 #else
 264 #   error "Unsupported platform"
 265 #endif
 266 }
 267 
 268 /* hpctimer_measure_overhead: Returns overhead of TSC reading (in tics). */
 269 static uint64_t hpctimer_measure_overhead(void)
 270 {
 271     enum {
 272         TSC_OVERHEAD_NTESTS = 10
 273     };
 274     int i;
 275     uint64_t count, overhead = (uint64_t)~0x01;
 276 
 277     /* Make warm-up passes and determine timer overhead */
 278     for (i = 0; i < TSC_OVERHEAD_NTESTS; i++) {
 279         count = hpctimer_gettsc();
 280         count = hpctimer_gettsc() - count;
 281         if (count < overhead) {
 282             overhead = count;
 283         }
 284     }
 285     return overhead;
 286 }
 287 
 288 /*
 289  * hpctimer_calibrate_adaptive: Returns number of TSC tics per second.
 290  *                              Adaptive algorithm based on sleep.
 291  */
 292 /*
 293 static uint64_t hpctimer_calibrate_adaptive(uint64_t overhead)
 294 {
 295     enum {
 296         TSC_CALIBRATE_NTESTS = 2
 297     };
 298     int i;
 299     uint64_t count, freq;
 300 
 301     freq = (uint64_t)(~0x01);
 302     for (i = 0; i < TSC_CALIBRATE_NTESTS; i++) {
 303         count = hpctimer_gettsc();
 304         sleep(1);
 305         count = hpctimer_gettsc() - count - overhead;
 306         if (count < 0)
 307             count = 0;
 308         if (count < freq) {
 309             freq = count;
 310             i = 0;
 311         }
 312     }
 313     return freq;
 314 }
 315 */
 316 
 317 /*
 318  * hpctimer_calibrate_sleep: Returns number of TSC tics per second.
 319  */
 320 static uint64_t hpctimer_calibrate_sleep(uint64_t overhead)
 321 {
 322     uint64_t count;
 323     int delay = 3;
 324 
 325     count = hpctimer_gettsc();
 326     sleep(delay);
 327     count = hpctimer_gettsc() - count - overhead;
 328     return count / delay;
 329 }
 330 
 331 /*
 332  * hpctimer_calibrate_loop: Returns number of TSC tics per second.
 333  */
 334 /*
 335 static uint64_t hpctimer_calibrate_loop(uint64_t overhead)
 336 {
 337     enum {
 338         TSC_CALIBRATE_NTESTS = 2
 339     };
 340     uint64_t count, countmin = (uint64_t)~0x01;
 341     struct timeval tv1, tv2;
 342     int i, j;
 343     __volatile__ int dummy = 0;
 344 
 345     for (i = 0; i < TSC_CALIBRATE_NTESTS; i++) {
 346         gettimeofday(&tv1, NULL);
 347         count = hpctimer_gettsc();
 348         for (j = 0; j < 10000000; j++) {
 349             dummy++;
 350         }
 351         count = hpctimer_gettsc() - count - overhead;
 352         gettimeofday(&tv2, NULL);
 353         if (count < 0)
 354             count = 0;
 355         if (count < countmin)
 356             countmin = count;
 357     }
 358     return countmin * 1000000 / (tv2.tv_sec * 1000000 + tv2.tv_usec -
 359                                  tv1.tv_sec * 1000000 - tv1.tv_usec);
 360 }
 361 */

/* [<][>][^][v][top][bottom][index][help] */