#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;
}