LoginSignup
3
3

More than 5 years have passed since last update.

Invariant TSC を 使う

Posted at

はじめに

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を示します。

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 は 64 bit 符号なし整数です。RDTSCP instruction で 読み出せます。

code 2

ENTER key 押下時 に TSC を読み出して 表示する codeを示します。
一定間隔で ENTER key を押下することで TSC の Frequency を推定できます。

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

腕時計を見ながら 10秒間隔でENTER keyを押下してみます。
TSC の差分は22*10^9です。秒に変換すると2.2GHzです。
実行した CPU は 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

正しい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 を選択してファイルに保存します。

1.png

テキスト内に次の行があります。

Max non-turbo ratio 22x

よってこのCPUの Invariant TSC Frequency は 22x100MHz = 2.2GHz であることが分かりました。

Linux

Linuxではrdmsrを使って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です。
よってこのCPUの Invariant TSC Frequency は 34x100MHz = 3.4GHz であることが分かりました。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3