GNU/Linux >> Linux の 問題 >  >> Linux

RDTSC を使用して CPU サイクルを取得する - RDTSC の値が常に増加するのはなぜですか?

TSC については、紛らわしい情報や間違った情報がたくさん出回っているので、その一部を整理してみようと思いました.

Intel が (オリジナルの Pentium CPU で) TSC を最初に導入したとき、(時間ではなく) サイクルをカウントすることが明確に文書化されていました。ただし、当時の CPU はほとんどが固定周波数で動作していたため、文書化された動作を無視して時間を測定するために使用する人もいました (特に Linux カーネル開発者)。彼らのコードは、(電力管理などのために)固定周波数で実行されない後の CPU で壊れました。その頃、他の CPU メーカー (AMD、Cyrix、Transmeta など) は混乱し、一部はサイクルを測定するために TSC を実装し、一部は時間を測定するために実装し、一部は (MSR を介して) 構成可能にしました。

その後、「マルチチップ」システムがサーバーでより一般的になりました。さらにその後、マルチコアが導入されました。これにより、異なるコアの TSC 値にわずかな違いが生じました (起動時間が異なるため)。しかし、より重要なことは、(電力管理やその他の要因により) 異なる速度で実行されている CPU が原因で、異なる CPU の TSC 値に大きな違いが生じることにもなりました。

最初から間違った使い方をしようとしていた人々 (サイクルではなく時間を測定するために使用していた人々) は多くの不満を漏らし、最終的には CPU メーカーに、TSC がサイクルではなく時間を測定するように標準化するよう説得しました。

もちろん、これは混乱でした。すべての 80x86 CPU をサポートする場合、TSC が実際に何を測定するかを判断するだけでも、多くのコードが必要です。また、さまざまな電源管理テクノロジ (SpeedStep などを含むが、スリープ状態なども含む) は、さまざまな CPU でさまざまな方法で TSC に影響を与える可能性があります。そのため、AMD は CPUID に「TSC 不変」フラグを導入して、時間を正しく測定するために TSC を使用できることを OS に伝えました。

最近のすべての Intel および AMD CPU は、しばらくの間このようなものでした.TSC は時間をカウントし、サイクルをまったく測定しません.つまり、サイクルを測定する場合は、(モデル固有の) パフォーマンス監視カウンターを使用する必要がありました。残念ながら、パフォーマンス監視カウンターはさらに混乱しています (モデル固有の性質と複雑な構成のため)。


スレッドが同じ CPU コア上にある限り、RDTSC 命令はラップアラウンドするまで増加する数値を返し続けます。 2GHz CPU の場合、これは 292 年後に発生するため、実際の問題ではありません。あなたはおそらくそれが起こるのを見ないでしょう.そんなに長生きするつもりなら、50 年ごとにコンピュータを再起動するようにしてください。

RDTSC の問題は、古いマルチコア CPU のすべてのコアで同じ時点で開始する保証がなく、古いマルチ CPU ボードのすべての CPU で同じ時点で開始するという保証がないことです。 .
最近のシステムでは通常、このような問題はありませんが、スレッドのアフィニティを 1 つの CPU でのみ実行するように設定することで、古いシステムでも問題を回避できます。これはアプリケーションのパフォーマンスには良くないため、通常は行うべきではありませんが、ティックを測定する場合は問題ありません。

(もう 1 つの「問題」は、多くの人が時間の測定に RDTSC を使用していることですが、これは そうではありません それは何をしますが、CPUサイクルが必要だと書いたので、それで問題ありません。 する場合 RDTSC を使用して時間を測定すると、節電やハイパーブーストなど、さまざまな周波数変更技術と呼ばれるものが作動したときに驚くことがあります。実際の時間については、clock_gettime syscall は、Linux では驚くほど優れています。)

rdtsc と書くだけです asm の中 これは私にとってはうまく機能し、あいまいな 16 進コードよりも読みやすくなっています。それが正しい 16 進コードであると仮定すると (また、クラッシュせず、増え続ける数値を返すこともないので、そのように思われます)、コードは適切です。

コードの一部にかかるティック数を測定したい場合は、ティック difference が必要です 、増加し続けるカウンターの2つの値を減算するだけです。 uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0; のようなもの
周囲のコードから分離された非常に正確な測定が必要な場合は、rdtsc を呼び出す前にシリアル化する必要があることに注意してください。つまり、パイプラインをストールします。 (または rdtscp を使用) これは、新しいプロセッサーでのみサポートされています)。すべての特権レベルで使用できる 1 つのシリアル化命令は cpuid です .

コメントの追加質問への回答:

コンピューターの電源を入れると、TSC はゼロから始まります (また、BIOS はすべての CPU のすべてのカウンターを同じ値にリセットしますが、数年前の一部の BIOS は確実にそうしませんでした)。

したがって、プログラムの観点からは、カウンターは「過去の未知の時間」を開始し、CPU が確認するすべてのクロックティックで常に増加します。そのため、そのカウンターを返す命令を今すぐ実行し、後で別のプロセスで実行すると、より大きな値が返されます (その間に CPU が中断またはオフにされていない限り)。カウンターが増え続けるため、同じプログラムの異なる実行はより大きな数値を取得します。いつも。

今、clock_gettime(CLOCK_PROCESS_CPUTIME_ID) は別問題です。これは、OS がプロセスに割り当てた CPU 時間です。プロセスが開始されると、ゼロから始まります。新しいプロセスもゼロから始まります。したがって、2 つのプロセスが相次いで実行されると、非常に類似した、または同一の数値が得られ、増加することはありません。

clock_gettime(CLOCK_MONOTONIC_RAW) RDTSC の動作に近い (一部の古いシステムでは RDTSC が実装されています)。増加し続ける値を返します。現在、これは通常、HPET です。しかし、これは本当に時間です 、目盛りではありません .コンピュータが低電力状態 (通常の 1/2 の周波数で実行中など) になった場合でも、まだ 同じペースで進みます。


すでに良い回答があり、デイモンは彼の回答でこれについて言及していますが、RDTSC の実際の x86 マニュアル (ボリューム 2、4-301) エントリからこれを追加します。

<ブロック引用>

プロセッサのタイムスタンプ カウンタ (64 ビット MSR) の現在の値を EDX:EAX レジスタにロードします。 EDX レジスタには MSR の上位 32 ビットがロードされ、EAX レジスタには下位 32 ビットがロードされます。 (Intel 64 アーキテクチャをサポートするプロセッサでは、RAX と RDX のそれぞれの上位 32 ビットがクリアされます。)

プロセッサは、クロック サイクルごとにタイムスタンプ カウンタ MSR を単調にインクリメントし、プロセッサがリセットされるたびに 0 にリセットします。 Intel® 64 and IA-32 Architectures Software Developer's Manual, Volume 3B の第 17 章の「Time Stamp Counter」を参照してください。 、タイム スタンプ カウンターの動作の具体的な詳細については。


Linux
  1. 最後にWindowsを使用したのはいつですか。

  2. Bashrcが現在のシェルがインタラクティブであるかどうかをチェックするのはなぜですか?

  3. Linux – Zramを使用する場合のVm.swappinessの適切な値は?

  1. Unix時間が1970-01-01から始まるのはなぜですか?

  2. System.currentTimeMillis は常に値 >=以前の呼び出しを返しますか?

  3. スリープ中のプロセスは同じ CPU 時間を取得しますか?

  1. Windows 10 VMがQEMU-KVMで常に100%のCPU使用率を示すのはなぜですか?

  2. ルートユーザーがSudo権限を必要とするのはなぜですか?

  3. setuid ビットの動作に一貫性がないのはなぜですか?