/* clock.c * Retrofitted to use thread-specific timers * and to get clock information from /proc/cpuinfo * (C) R. E. Bryant, 2010 * */ /* When this constant is not defined, uses time stamp counter */ #define USE_POSIX 0 /* Choice to use cpu_gettime call or Intel time stamp counter directly */ #include #include #include #ifdef USE_POSIX #include #endif #include "clock.h" int gverbose = 1; /* Keep track of clock speed */ double cpu_ghz = 0.0; /* Get megahertz from /etc/proc */ #define MAXBUF 512 /* Induce load on processor to have it set clock rate to maximum */ volatile int incr = 1; volatile int val = 0; /* How many steps to run? */ #define STEP_CNT (1<<30) void burn_cpu() { int i; val = 0; incr = random() & 0x7; for (i = 0; i < STEP_CNT; i++) val *= incr; } double core_mhz(int verbose) { static char buf[MAXBUF]; FILE *fp = fopen("/proc/cpuinfo", "r"); cpu_ghz = 0.0; if (!fp) { fprintf(stderr, "Can't open /proc/cpuinfo to get clock information\n"); cpu_ghz = 1.0; return cpu_ghz * 1000.0; } while (fgets(buf, MAXBUF, fp)) { if (strstr(buf, "cpu MHz")) { double cpu_mhz = 0.0; sscanf(buf, "cpu MHz\t: %lf", &cpu_mhz); // printf("Read %.4f MHz from line:%s", cpu_mhz, buf); cpu_ghz = cpu_mhz / 1000.0; break; } } fclose(fp); if (cpu_ghz == 0.0) { fprintf(stderr, "Can't open /proc/cpuinfo to get clock information\n"); cpu_ghz = 1.0; return cpu_ghz * 1000.0; } if (verbose) { printf("Processor Clock Rate ~= %.4f GHz (extracted from file)\n", cpu_ghz); } return cpu_ghz * 1000; } double mhz(int verbose) { /* Run processor a bit to get clock rate set */ burn_cpu(); double val = core_mhz(verbose); return val; } #ifdef USE_POSIX /* Simulate counters by using nanosecond timers and then converting to clock cycles */ struct timespec last_time; /* Use thread clock */ #define CLKT CLOCK_THREAD_CPUTIME_ID void start_counter() { if (cpu_ghz == 0.0) mhz(gverbose); if (clock_gettime(CLKT, &last_time) != 0) { fprintf(stderr, "Couldn't get time\n"); exit(1); } } double get_counter() { struct timespec new_time; double delta_nsecs = 0.0; if (clock_gettime(CLKT, &new_time) != 0) { fprintf(stderr, "Couldn't get time\n"); exit(1); } delta_nsecs = 1e9 * (new_time.tv_sec - last_time.tv_sec) + (new_time.tv_nsec - last_time.tv_nsec); return delta_nsecs * cpu_ghz; } #else /* !USE_POSIX */ /* Use x86 cycle counter */ /* Initialize the cycle counter */ static unsigned cyc_hi = 0; static unsigned cyc_lo = 0; /* Set *hi and *lo to the high and low order bits of the cycle counter. Implementation requires assembly code to use the rdtsc instruction. */ void access_counter(unsigned *hi, unsigned *lo) { asm("rdtsc; movl %%edx,%0; movl %%eax,%1" /* Read cycle counter */ : "=r" (*hi), "=r" (*lo) /* and move results to */ : /* No input */ /* the two outputs */ : "%edx", "%eax"); } /* Record the current value of the cycle counter. */ void start_counter() { access_counter(&cyc_hi, &cyc_lo); } /* Return the number of cycles since the last call to start_counter. */ double get_counter() { unsigned ncyc_hi, ncyc_lo; unsigned hi, lo, borrow; double result; /* Get cycle counter */ access_counter(&ncyc_hi, &ncyc_lo); /* Do double precision subtraction */ lo = ncyc_lo - cyc_lo; borrow = cyc_lo > ncyc_lo; hi = ncyc_hi - cyc_hi - borrow; result = (double) hi * (1 << 30) * 4 + lo; return result; } #endif /* USE_POSIX */