Intel CPUs have a counter that counts up at a constant clock. It is called Invariant TSC (Time Stamp Counter). I will explain how to use this. sample code is made with gcc of MINGW64.
Invariant TSC available Flag You can check if Invariant TSC is available by using the CPUID instruction. Set the eax register to 0x80000007 and execute the cpuid instruction. Bit 08 of edx is that flag.
code 1 Shows the sample code to read the Invariant TSC available Flag.
makefile
CFLAGS=-I. -Wall -Werror -O2
INCS=
OBJS=test.o
LIBS=
TARGET=test
all: $(TARGET)
%.o: %.c $(INCS)
$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.s $(INCS)
$(CC) $(CFLAGS) -c -o $@ $<
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
clean:
rm -rf $(TARGET) *.o
test.s
# rcx(1st) rdx(2nd) r8(3rd) r9(4th)
.globl main
main:
subq $40, %rsp
call __main
# EDX Bit 08: Invariant TSC available if 1.
movl $0x80000007, %eax
cpuid
# printf
leaq s0, %rcx # 1st
movl %edx, %edx # 2nd
call printf
xorl %eax, %eax
addq $40, %rsp
ret
.section .rodata
s0: .asciz "cpuid 0x80000007 edx : 0x%08X \n"
console
$ make && ./test.exe
cpuid 0x80000007 edx : 0x00000100
RDTSCP instruction Invariant TSC is a 64-bit unsigned integer. It can be read with the RDTSCP instruction.
code 2 Indicates the code that reads and displays the TSC when the ENTER key is pressed. You can estimate the TSC Frequency by pressing the ENTER key at regular intervals.
test.c
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
static inline uint64_t RDTSCP();
static void print_tsc();
int main(int argc, char* argv[])
{
while(1) {
char buf[4];
print_tsc();
printf("press ENTER key.");
fflush(stdout);
fgets(buf, 4, stdin);
}
return 0;
}
static inline uint64_t RDTSCP()
{
uint32_t hi, lo, aux;
__asm__ volatile("rdtscp" : "=a" (lo), "=d" (hi), "=c" (aux));
return ((uint64_t)hi << 32) | lo;
}
static void print_tsc()
{
#define MAXU64 (0xFFFFFFFFFFFFFFFF)
static uint64_t prev = MAXU64;
uint64_t tsc, diff = 0;
tsc = RDTSCP();
if (prev != MAXU64) {
diff = tsc - prev;
printf("tsc=%" PRIu64 " diff=%" PRIu64 "\n", tsc, diff);
} else {
printf("tsc=%" PRIu64 "\n", tsc);
}
prev = tsc;
}
While looking at your watch, try pressing the ENTER key at 10-second intervals. The difference in TSC is 22 * 10 ^ 9. Converted to seconds, it is 2.2 GHz. The CPU I ran was Intel (R) Core (TM) i5-5200U CPU @ 2.20GHz.
console
$ make && ./test.exe
tsc=141732173016637
press ENTER key.
tsc=141752243551743 diff=20070535106
press ENTER key.
tsc=141774064869318 diff=21821317575
press ENTER key.
tsc=141796080991589 diff=22016122271
press ENTER key.
Invariant TSC Frequency Machine specific registers (MSRs) must be read to get the correct Invariant TSC Frequency. It is a little difficult because you need to be in privileged mode to read MSRs. Therefore, in code 2, Frequency was estimated by performing Invariant TSC measurements at regular intervals.
The Invariant TSC Frequency is the 0xCE 15: 8 bit ** Maximum Non-Turbo Ratio ** multiplied by 100MHz.
0xCE 15:8 bit Maximum Non-Turbo Ratio (R/O) The is the ratio of the frequency that invariant TSC runs at. Frequency = ratio * 100 MHz.
Windows You can read MSRs on CPU-Z. Select Save Report as .TXT to save to a file.
There is the following line in the text:
Max non-turbo ratio 22x
Therefore, it was found that the Invariant TSC Frequency of this CPU is 22x100MHz = 2.2GHz.
Linux On Linux, use rdmsr to read MSRs.
console
$ cat /proc/cpuinfo | grep "model name"
model name : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
$ sudo apt-get install msr-tools
$ sudo modprobe msr
$ sudo rdmsr 0xce
80838f1012200
0x22 = 34. Therefore, it was found that the Invariant TSC Frequency of this CPU is 34x100MHz = 3.4GHz.