Use Invariant TSC

Introduction

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.

1.png

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.

Recommended Posts

Use Invariant TSC
Use DeepLabCut
Use pycscope
Use collections.Counter
Use: Django-MySQL
Use Pygments.rb
Use Numpy
use pandas-ply
Use GitPython
Use Miniconda