質問
#include <stdio.h>
static inline unsigned long long tick()
{
unsigned long long d;
__asm__ __volatile__ ("rdtsc" : "=A" (d) );
return d;
}
int main()
{
long long res;
res=tick();
res=tick()-res;
printf("%d",res);
return 0;
}
-O0 -O1 -O2 -O3の最適化を備えたGCCでこのコードをコンパイルしました。そして、私はいつも2000〜2500サイクルを取得します。誰かがこの出力の理由を説明できますか?これらのサイクルを使う方法は?
最初の関数「ティック」が間違っています。これは正しいです.
関数「ティック」の別のバージョン
static __inline__ unsigned long long tick()
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
これは-O3のアセンブリコードです
.file "rdtsc.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
subl $40, %esp
movl %ecx, -16(%ebp)
movl %ebx, -12(%ebp)
movl %esi, -8(%ebp)
movl %edi, -4(%ebp)
#APP
# 6 "rdtsc.c" 1
rdtsc
# 0 "" 2
#NO_APP
movl %edx, %edi
movl %eax, %esi
#APP
# 6 "rdtsc.c" 1
rdtsc
# 0 "" 2
#NO_APP
movl %eax, %ecx
movl %edx, %ebx
subl %esi, %ecx
sbbl %edi, %ebx
movl %ecx, 4(%esp)
movl %ebx, 8(%esp)
movl $.LC0, (%esp)
call printf
movl -16(%ebp), %ecx
xorl %eax, %eax
movl -12(%ebp), %ebx
movl -8(%ebp), %esi
movl -4(%ebp), %edi
movl %ebp, %esp
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Debian 4.3.2-1.1) 4.3.2"
.section .note.GNU-stack,"",@progbits
これはCPUです
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 4
model name : Intel(R) Xeon(TM) CPU 3.00GHz
stepping : 3
cpu MHz : 3000.105
cache size : 2048 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 5
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss constant_tsc up pebs bts pni
bogomips : 6036.62
clflush size : 64
解決
さまざまなIntel CPUで実行されているいくつかのLinuxディストリビューションでコードを試しました(確かに、使用しているように見えるPentium 4 HT 630よりも最近のものです)。これらのすべてのテストで、25〜50サイクルの値を取得しました。
すべての証拠と一致している私の唯一の仮説は、裸の金属ではなく仮想マシン内でオペレーティングシステムを実行しており、TSCが仮想化されているということです。
他のヒント
多くの理由を取得する理由はいくつかあります:
- OSはコンテキスト切り替えを行い、プロセスが眠りにつくようになりました。
- ディスクシークが発生し、プロセスが眠りについた。
- …あなたのプロセスが無視される理由に関するいくつかの理由のいずれか。
ご了承ください rdtsc
作業なしでのタイミングについては特に信頼できません。
- プロセッサの速度は変化する可能性があるため、サイクルの長さ(秒単位で測定する場合)が変化します。
- さまざまなプロセッサが、時間内に特定の瞬間に対してTSCの値が異なる場合があります。
ほとんどの操作システムには、高精度のクロックまたはタイミング方法があります。 clock_gettime
たとえば、Linux、特に単調な時計。 (壁1杯と単調な時計の違いも理解してください:壁の時計はUTCであっても後方に移動できます。)窓の上では、推奨は QueryHighPerformanceCounter
. 。通常、これらのクロックは、ほとんどのニーズに十分な精度を提供します。
また、アセンブリを見ると、あなたは32ビットの答えを得ているように見えます:私は見えません %edx
その後救われる rdtsc
.
あなたのコードを実行して、私は120-150 nsからタイミングを取得します clock_gettime
使用 CLOCK_MONOTONIC
, 、およびRDTSCの70-90サイクル(フルスピードで〜20 nsですが、プロセッサがクロックダウンされていると思われますが、それは本当に約50 nsです)。 (aで ラップトップデスクトップ(darn ssh、私がオンにしていたマシンを忘れました!)それは約20%のCPU使用です)
ユーザースペースでのRDTSCのOSを無効にした実行のようです。アプリケーションはカーネルとバックに切り替える必要があり、多くのサイクルが必要です。
これは、Intelソフトウェア開発者のマニュアルからのものです。
保護されたまたは仮想8086モードの場合、レジスタCR4のタイムスタンプ無効化(TSD)フラグは、次のようにRDTSC命令の使用を制限します。 TSDフラグが明確な場合、RDTSC命令は任意の特権レベルで実行できます。フラグが設定されている場合、命令は特権レベル0でのみ実行できます。(実際のアドレスモードの場合、RDTSC命令は常に有効になります。)
編集:
AIXのコメントに答えると、なぜTSDがここでTSDが理由である可能性が高いのかを説明します。
私は、プログラムが通常よりも長く単一の命令を実行するこれらの可能性だけを知っています。
- いくつかのエミュレータの下で実行される、
- 自己修正コードを使用して、
- コンテキストスイッチ、
- カーネルスイッチ。
最初の2つの理由は、通常、数百サイクル以上の実行を遅らせることはできません。 2000-2500サイクルは、コンテキスト/カーネルスイッチの方が典型的です。しかし、同じ場所でコンテキストスイッチを数回キャッチすることは実際には不可能です。したがって、カーネルスイッチにする必要があります。つまり、どちらのプログラムがデバッガーで実行されているか、RDTSCがユーザーモードでは許可されていません。
OSがRDTSCを無効にする可能性の高い理由は、セキュリティかもしれません。暗号化プログラムをクラックするためにRDTSCを使用する試みがありました。
命令キャッシュミス? (これは私の推測です)
また、おそらく、
仮想化システムのハイパーバイザーに切り替えますか?プログラムブートストラップの残骸(同じCPUでのネットワークアクティビティを含む)?
タナトスへ:2008年よりも最近のシステムでは、RDTSC()は壁の時計であり、周波数ステップで変化しません。
この小さなコードを試すことができますか?
int main()
{
long long res;
fflush(stdout); // chnage the exact timing of stdout, in case there is something to write in a ssh connection, together with its interrupts
for (int pass = 0; pass < 2; pass++)
{
res=tick();
res=tick()-res;
}
printf("%d",res); // ignore result on first pass, display the result on second pass.
return 0;
}
ただのアイデア - おそらく、これらの2つのRDTSC命令は異なるコアで実行されますか? RDTSC値は、コア間でわずかに異なる場合があります。