はじめに
Intel CPU には 一定のクロックでカウントアップするカウンタがあります。
Invariant TSC (Time Stamp Counter) といいます。 この使い方を説明します。
sample code は MINGW64 の gcc で make します。
Invariant TSC available Flag
CPUID instruction を使うことで Invariant TSC が 利用可能かどうかをチェックできます。
eax register に 0x80000007 を設定して cpuid 命令を実行します。edx の Bit 08 が そのフラグです。
code 1
Invariant TSC available Flag を読み出す sample codeを示します。
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
# 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"
$ make && ./test.exe
cpuid 0x80000007 edx : 0x00000100
RDTSCP instruction
Invariant TSC は 64 bit 符号なし整数です。RDTSCP instruction で 読み出せます。
code 2
ENTER key 押下時 に TSC を読み出して 表示する codeを示します。
一定間隔で ENTER key を押下することで TSC の Frequency を推定できます。
#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;
}
腕時計を見ながら 10秒間隔でENTER keyを押下してみます。
TSC の差分は22*10^9です。秒に変換すると2.2GHzです。
実行した CPU は Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz です。
$ 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
正しいInvariant TSC Frequency を取得するには Machine specific registers(MSRs)を読み出す必要があります。
MSRsを読み出すには特権モードである必要がある為、少々敷居が高いです。
そのため code 2 では 一定間隔 の Invariant TSC 計測をすることで Frequencyを推定しました。
0xCE 15:8 bit の Maximum Non-Turbo Ratio に 100MHz を掛けたものが Invariant TSC Frequencyです。
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
CPU-Z で MSRs を読み出せます。Save Report as .TXT を選択してファイルに保存します。
テキスト内に次の行があります。
Max non-turbo ratio 22x
よってこのCPUの Invariant TSC Frequency は 22x100MHz = 2.2GHz であることが分かりました。
Linux
Linuxではrdmsrを使ってMSRsを読み出します。
$ 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です。
よってこのCPUの Invariant TSC Frequency は 34x100MHz = 3.4GHz であることが分かりました。