#include <stdio.h> #include <sys/time.h> /* I wanted to know how fast the TSC on my laptop increments. It's a * 700MHz PIII, and /proc/cpuinfo says it's "697.007" MHz, so I * expected it would increment at 697.007 million counts per second, * more or less. So I wrote this program to find out. * * Much to my surprise, when the laptop is mostly idle, the number * varies wildly, usually staying between 300 million and 500 million * per second, but occasionally going above or below this range; and * when the laptop is loaded, it stays between 547.5 and 547.7 million * per second. When I run it on Panacea, which has a 1.8GHz dual-core * Opteron, it's consistently between 1799 and 1801 million ticks per * second. * * The Intel manual (Intel document 253669) section 18.10 ("Time-stamp * counter") explains that on old CPUs, the TSC doesn't increase at a * constant rate, but rather varies with the clock speed. I don't * understand what is going on with my laptop; apparently it's * actually running at 550MHz rather than 700, and maybe the TSC * doesn't increment while the CPU is halted? * * ...after further testing, it seems to sometimes be 700 million * ticks per second, and sometimes 550. */
double now() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec / 1000000.0; } unsigned rdtsc() { register unsigned rv asm ("%eax"); /* If we don't specify "volatile", GCC may optimize away the asm * expression or the call to rdtsc() */ asm volatile ("rdtsc" : "=r" (rv) : : "edx"); return rv; } int main(int argc, char **argv) { unsigned start, end; double starttime_inner, starttime_outer, endtime_inner, endtime_outer; starttime_outer = now(); start = rdtsc(); starttime_inner = now(); usleep(50000); /* 50ms should be plenty long enough */ endtime_inner = now(); end = rdtsc(); endtime_outer = now(); printf("Got %d RDTSC ticks in %.3f-%.3f ms, ", end - start, (endtime_inner - starttime_inner) * 1000, (endtime_outer - starttime_outer) * 1000); printf("for %.3f-%.3f million ticks per second.\n", (end - start) / (endtime_outer - starttime_outer) / 1000 / 1000, (end - start) / (endtime_inner - starttime_inner) / 1000 / 1000); return 0; }