「最近のRDTSC命令は実際のCPUクロックを数えていない」という記事を見つけて、本当にそんなことがあるのかと気になったので、確認してみることにした。
元記事にあった Linux OS/GNU CコンパイラでのRDTSC命令 という場所を参考にしたのだが、そのままでは使えないようだし、どうもうまく動かなかった。
#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_ */
#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;
}
インラインアセンブラがうまく展開されないらしく、リンクに失敗する。何かが足りないらしい。
#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付けて、その他ちょこっとイジった程度だけど、エラーが出なくなったようだ。本当にコレでいいのか、イマイチ自信が無いが「動いてるのでヨシ!」
#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)
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) を用意した。
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]) を用意した。
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)
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)
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)
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で動作中って、どんだけサバ読んでるのよ。
AMD Ryzen 5 2400G 4-Core Processor @3600.000MHz (on Debian/GNU [Native] Linux)
AsrockMania@deskmini-a300:~/src/rdtsc$ ./rdtsctest
measured time : 18227016[clock] in 0.5132Sec.
estimate clock speed = 3645403200Hz
おや、同じマシンにDebianを入れてみたら、ちゃんと3.6GHz付近で動作してるという結果が上がってきた。
バックグラウンドで分散コンピューティングクライアントが稼働中なのでCPUはおそらくフルパワーで作動中。
一応 lscpu でCPUの状態をチェックしてみる。
AsrockMania@deskmini-a300:~/src/rdtsc$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 43 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Vendor ID: AuthenticAMD
Model name: AMD Ryzen 5 2400G with Radeon Vega Graphics
CPU family: 23
Model: 17
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
Stepping: 0
Frequency boost: enabled
CPU(s) scaling MHz: 98%
CPU max MHz: 3600.0000
CPU min MHz: 1600.0000
BogoMIPS: 7200.12
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat
pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe
1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_ap
icid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 s
se4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy sv
m extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw skin
it wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwait
x cpb hw_pstate ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rds
eed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero irpe
rf xsaveerptr arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clea
n flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_
vmload vgif overflow_recov succor smca sev sev_es
Virtualization features:
Virtualization: AMD-V
Caches (sum of all):
L1d: 128 KiB (4 instances)
L1i: 256 KiB (4 instances)
L2: 2 MiB (4 instances)
L3: 4 MiB (1 instance)
NUMA:
NUMA node(s): 1
NUMA node0 CPU(s): 0-7
Vulnerabilities:
Gather data sampling: Not affected
Itlb multihit: Not affected
L1tf: Not affected
Mds: Not affected
Meltdown: Not affected
Mmio stale data: Not affected
Reg file data sampling: Not affected
Retbleed: Mitigation; untrained return thunk; SMT vulnerable
Spec rstack overflow: Mitigation; safe RET
Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitizati
on
Spectre v2: Mitigation; Retpolines; IBPB conditional; STIBP disabled; RSB fill
ing; PBRSB-eIBRS Not affected; BHI Not affected
Srbds: Not affected
Tsx async abort: Not affected
[CPU(s) scaling MHz: 98%] なので、ほぼフルスピードらしい。