8
#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
4

5 に答える 5

11

さまざまなIntelCPUで実行されているいくつかのLinuxディストリビューションでコードを試しました(確かに、使用しているように見えるPentium 4 HT 630よりもすべて新しいものです)。これらすべてのテストで、25〜50サイクルの値が得られました。

すべての証拠と一致する私の唯一の仮説は、オペレーティングシステムをベアメタルではなく仮想マシン内で実行しており、TSCが仮想化されているというものです。

于 2011-11-30T10:50:33.917 に答える
7

多数を取得する理由はいくつもあります。

  • OSがコンテキストスイッチを実行し、プロセスがスリープ状態になりました。
  • ディスクシークが発生し、プロセスがスリープ状態になりました。
  • …プロセスが無視される理由に関する多くの理由のいずれか。

rdtsc次の理由により、作業なしのタイミングでは特に信頼性が低いことに注意してください。

  • プロセッサの速度は変化する可能性があるため、サイクルの長さ(秒単位で測定した場合)が変化します。
  • プロセッサが異なれば、特定の瞬間のTSCの値も異なる場合があります。

ほとんどのオペレーティングシステムには、高精度のクロックまたはタイミング方式があります。clock_gettimeたとえばLinuxでは、特に単調な時計です。(壁掛け時計と単調時計の違いも理解してください。壁掛け時計はUTCでも逆方向に移動できます。)Windowsでは、推奨事項はQueryHighPerformanceCounterです。通常、これらのクロックは、ほとんどのニーズに対して十分な精度を提供します。


また、アセンブリを見ると、32ビットの答えしか得られていないようです。の%edx後に保存されることはありませんrdtsc


コードを実行すると、を使用する場合は120〜150 ns、rdtscをclock_gettime使用するCLOCK_MONOTONIC場合は70〜90サイクルのタイミングが得られます(フルスピードで約20 nsですが、プロセッサがクロックダウンしていると思われます。これは実際には約50 nsです)。(ラップトップデスクトップ(SSH、私がどのマシンを使用していたかを忘れてしまいました!)では、CPUの使用率はほぼ一定です)マシンが停止していないことを確認しますか?

于 2011-11-30T09:31:14.243 に答える
4

OSがユーザースペースでのRDTSCの実行を無効にしたようです。また、アプリケーションはカーネルに切り替えて元に戻す必要があり、これには多くのサイクルが必要です。

これは、インテルソフトウェア開発者マニュアルからのものです。

保護モードまたは仮想8086モードの場合、レジスタCR4のタイムスタンプ無効(TSD)フラグは、RDTSC命令の使用を次のように制限します。TSDフラグがクリアされている場合、RDTSC命令は任意の特権レベルで実行できます。フラグが設定されている場合、命令は特権レベル0でのみ実行できます(リアルアドレスモードの場合、RDTSC命令は常に有効になります)。

編集:

aixのコメントに答えて、TSDがここでの理由である可能性が最も高い理由を説明します。

私は、プログラムが通常よりも長く単一の命令を実行する可能性についてのみ知っています。

  1. いくつかのエミュレーターの下で実行し、
  2. 自己修正コードを使用して、
  3. コンテキストスイッチ、
  4. カーネルスイッチ。

最初の2つの理由は、通常、実行を数百サイクル以上遅らせることはできません。2000〜2500サイクルは、コンテキスト/カーネルの切り替えでより一般的です。ただし、同じ場所でコンテキストスイッチを複数回キャッチすることは事実上不可能です。したがって、カーネルスイッチである必要があります。これは、プログラムがデバッガーで実行されているか、RDTSCがユーザーモードで許可されていないことを意味します。

OSがRDTSCを無効にする最も可能性の高い理由は、セキュリティである可能性があります。RDTSCを使用して暗号化プログラムを解読する試みがありました。

于 2011-11-30T09:46:39.460 に答える
1

命令キャッシュミス?(これは私の推測です)

また、おそらく、

仮想化システムでハイパーバイザーに切り替えますか?プログラムブートストラップの残骸(同じ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;
}
于 2012-10-31T05:05:37.533 に答える
0

ただのアイデア-おそらくこれらの2つのrdtsc命令は異なるコアで実行されますか?rdtsc値は、コア間でわずかに異なる場合があります。

于 2011-11-30T09:36:07.480 に答える