1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RDTSC命令を使い、いまどきのCPUでクロックを測定してみる

Last updated at Posted at 2022-02-11

最近のRDTSC命令は実際のCPUクロックを数えていない」という記事を見つけて、本当にそんなことがあるのかと気になったので、確認してみることにした。

元記事にあった Linux OS/GNU CコンパイラでのRDTSC命令 という場所を参考にしたのだが、そのままでは使えないようだし、どうもうまく動かなかった。

rdtsc.h (original)
#ifndef RDTSC_H_
#define RDTSC_H_

inline unsigned long long rdtsc() {
    unsigned long long ret;
    __asm__ volatile ("rdtsc" : "=A" (ret));
    return ret;
}

#endif /* RDTSC_H_ */
rdtsctest.c (original)
#include "rdtsc.h"
#include <stdio.h>

int measure_func()
{
    unsigned long long start = rdtsc();
    to_be_measured();
    unsigned long long stop = rdtsc();
    printf("measured time : %I64d [clock]\n", stop - start);

    return 0;
}

インラインアセンブラがうまく展開されないらしく、リンクに失敗する。何かが足りないらしい。

rdtsc.h (modified)
#ifndef RDTSC_H_
#define RDTSC_H_

static inline unsigned long long rdtsc(void) {
        unsigned long long ret;
        __asm__ volatile ("rdtsc" : "=A" (ret));
        return ret;
}
#endif /* RDTSC_H_ */

static付けて、その他ちょこっとイジった程度だけど、エラーが出なくなったようだ。本当にコレでいいのか、イマイチ自信が無いが「動いてるのでヨシ!」

rdtsctest.c (modified)
#include "rdtsc.h"
#include <stdio.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>

#define NANOSLEEP_TIME 5000000 //MEASURE TIME

void to_be_measured(void)
{
        struct timespec ts;

        ts.tv_sec = 0;
        ts.tv_nsec = NANOSLEEP_TIME;
        nanosleep(&ts, NULL);
}

unsigned long long measure_func(void)
{
        unsigned long long start, stop, diff;
        start = rdtsc();
        to_be_measured(); //WAIT
        stop = rdtsc();
        diff = stop - start;
        printf("measured time : %" PRIu64 "[clock] in ", diff);

        return diff;
}

int main(void)
{
        unsigned long long clockcount;
        struct timeval t0;
        struct timeval t1;

        gettimeofday(&t0, NULL);

        clockcount = measure_func();

        gettimeofday(&t1, NULL);

        if (t1.tv_usec < t0.tv_usec) {
                printf("%d.%d", t1.tv_sec - t0.tv_sec - 1, 1000000 + t1.tv_usec - t0.tv_usec);
        }
        else {
                printf("%d.%d", t1.tv_sec - t0.tv_sec, t1.tv_usec - t0.tv_usec);
        }
        printf("Sec.\n estimate clock speed = %" PRIu64 "Hz\n", (1000000000 / NANOSLEEP_TIME) * clockcount);
        return 0;
}

main()が存在しなかったので追加。to_be_measured()も中身が定義されていなかったので実装。中身にnanosleep()を使い、五百万ナノ秒(5mSec)のウェイトを入れてみた。

経過時間の測定にはLinuxCのサイトを参考にgettimeofday()を使用。
この関数は、マイクロ秒単位でしか計測できないのが注意点らしい。

measure_func()を符号なし64bit整数型でrdtscを読んだ差分を返すよう改造。
printfを使用して、符号なしlong long intをどのようにフォーマットしますか?」という記事を参考に、
unsigned long long を表示するprintf用マクロ(PRIu64)をフォーマットに追加。<inttypes.h>をincludeすると使えるらしい。

さっそく、先日組んだ Debian/GNU Linux(bookworm)機上でテストしてみた。
Intel Core i5-12400F (on Native Linux)

test1_run
folding@home:~/src/rdtsc$ ./a.out
measured time : 12628852[clock] in 0.5093Sec.
 estimate clock speed = 2525770400Hz

正確さを詰めてないので誤差は結構あるだろうけど、実効クロックが2.52GHz辺りと算出できているので、一応それなりに動いてはいるようだ。

では次に、32bitのノートパソコンで測定してみよう。
Intel(R) Core(TM)2 CPU @1.83GHz (on Native Linux) を用意した。

test2_32run
oldmachine@ia32linux:~/src/rdtsc$ ./a.out 
measured time : 9318650[clock] in 0.5215Sec.
 estimate clock speed = 1863730000Hz

特にスリープも省電力も使ってないはずで、1.86GHzか。
確かにそれっぽいデータを吐くようだ。
64bit機でも32bit機でも、ソースに一切手を加える必要がないのは有り難い。

比較のため、このマシンを64bit Linuxで動かしてみる。
Intel(R) Core(TM)2 CPU @1.83GHz (on Native Linux[AMD64]) を用意した。

test2_64run
oldmachine@amd64linux:~/src/rdtsc$ ./a.out 
measured time : 9391107[clock] in 0.5265Sec.
 estimate clock speed = 1878221400Hz

きちんと1.8GHzで動いてることになってるらしいことは分かった。

第八世代インテルCore i5 プロセッサでも走らせてみよう。
WSL上のUbuntuでコンパイルしてみると、派手に5つぐらいWarningが表示された。
ちゃんと動くのかな。long intやlong long intの扱いが合って無いっぽいが。
とりあえずリンクまで通ったようなので実行バイナリは生きているようだ。
Intel(R) Core(TM) i5 CPU @2.30GHz (on WSL1)

test3_run
win10wsl@DESKTOP-Honyarara:~/src/rdtsc$ ./a.out
measured time : 11774144[clock] in 0.5379Sec.
 estimate clock speed = 2354828800Hz

なるほど、2.35GHzなら確かにそれっぽい周波数だと思う。
だが、WSLと同時にWindows上で動かしているCore Temp 1.17で実測した動作周波数は2.99~3GHzを指している。明らかに数値が違うんですけど?

では、素晴らしい性能で話題をさらったRyzenで動かすとどうなるのか。
こちらはWSLでもDebian入り。コンパイルエラーもウォーニングも出なかった。
AMD Ryzen 5 3600X 6-Core Processor @3800.000MHz (on WSL1)

test4_run
win10wsl@DESKTOP-RYZEN:~/src/rdtsc$ ./a.out
measured time : 22547414[clock] in 0.6225Sec.
 estimate clock speed = 4509482800Hz

あれ、4.5GHzって速すぎるんじゃね?(WSL上なのでいろいろありそうだが)
Ryzen Masterで自動オーバークロックする設定だけど、モニタしてると3.8GHz台を上下していて、4GHzなんて凄い速さは出てないぞ。

もう1台、BIOS上げてから全然スピードが出なくなった DeskMini A300の測定結果を見てみよう。酷使しすぎたせいか、おそろしく速度がダウンしていて、最近は定格ですら動かない状態。
Ubuntu入りで、ソースをコンパイルするとWarningが報告される。DebianではWarningが出てこないところを見ると、どうもUbuntu特有の設定が関係しているようだ。
AMD Ryzen 5 2400G 4-Core Processor @3600.000MHz (on WSL1)

test5_run
win10wsl@DESKTOP-RYZEN:~/src/rdtsc$ ./a.out
measured time : 21212730[clock] in 0.6286Sec.
 estimate clock speed = 4242546000Hz

君、Ryzen Master上だと定格の3.6GHzですら走らないレベルの2400G だよね?
4.25GHzで動作中って、どんだけサバ読んでるのよ。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?