SOでこの投稿を見ました。これには、最新のCPUサイクルカウントを取得するCコードが含まれています。
C/C++ Linux x86_64でのCPUサイクル数ベースのプロファイリング
このコードをC++で使用する方法はありますか(WindowsおよびLinuxソリューションを歓迎します)? Cで書かれていますが(CはC++のサブセットです)、このコードがC++プロジェクトで機能するかどうかはわかりませんが、そうでない場合はどのように翻訳しますか?
私はx86-64を使用しています
EDIT2:
この関数は見つかりましたが、VS2010にアセンブラーを認識させることができません。何も含める必要がありますか? (私はuint64_t
からlong long
Windowsの場合...?)
static inline uint64_t get_cycles()
{
uint64_t t;
__asm volatile ("rdtsc" : "=A"(t));
return t;
}
EDIT3:
上記のコードからエラーが発生します:
「エラーC2400:「opcode」のインラインアセンブラ構文エラー。「データ型」が見つかりました」
誰か助けてくれますか?
GCC 4.5以降では、__rdtsc()
組み込み関数がMSVCとGCCの両方でサポートされるようになりました。
ただし、必要なインクルードは異なります。
_#ifdef _WIN32
#include <intrin.h>
#else
#include <x86intrin.h>
#endif
_
GCC 4.5以前の元の答えは次のとおりです。
私のプロジェクトの1つから直接引き出しました:
_#include <stdint.h>
// Windows
#ifdef _WIN32
#include <intrin.h>
uint64_t rdtsc(){
return __rdtsc();
}
// Linux/GCC
#else
uint64_t rdtsc(){
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}
#endif
_
これは GNU C拡張asm コンパイラーに伝えます:
volatile
:出力は入力の純粋な関数ではありません(したがって、古い結果を再利用するのではなく、毎回再実行する必要があります)。"=a"(lo)
および"=d"(hi)
:出力オペランドは固定レジスタです:EAXおよびEDX。 ( x86マシンの制約 )。 x86 rdtsc
命令はその64ビット結果をEDX:EAXに入れるため、コンパイラーが_"=r"
_で出力を選択できるようになりません。結果をCPUに要求する方法はありません。どこか他の。((uint64_t)hi << 32) | lo
_-両方の32ビット半分を64ビットにゼロ拡張し(loとhiはunsigned
であるため)、論理的に+ ORを単一の64にシフトしますビットC変数。 32ビットコードでは、これは単なる再解釈です。値はまだ32ビットのレジスタのペアにとどまります。 64ビットコードでは、上位半分が最適化されない限り、通常は実際のシフト+ OR asm命令を取得します。(編集者注:_unsigned long
_の代わりに_unsigned int
_を使用した場合、おそらくこれはより効率的です。コンパイラはlo
が既にRAXにゼロ拡張されていることを認識します。上半分がゼロであったことを知っているので、異なる方法をマージしたい場合は_|
_と_+
_は同等です。いい仕事。)
https://gcc.gnu.org/wiki/DontUseInlineAsm 回避できる場合しかし、インラインasmを使用する古いコードを理解する必要がある場合、このセクションが有用であり、組み込み関数で書き直すことができれば幸いです。 https://stackoverflow.com/tags/inline-Assembly/info も参照してください
VC++は、インラインアセンブリにまったく異なる構文を使用しますが、32ビットバージョンのみです。 64ビットコンパイラは、インラインアセンブリをまったくサポートしていません。
この場合、それはおそらく同様です-rdtsc
には、タイミングコードシーケンスに関して(少なくとも)2つの大きな問題があります。最初(ほとんどの命令と同様)順不同で実行できるため、短いコードシーケンスの時間を計ろうとしている場合、そのコードの前後のrdtsc
が両方ともその前、または両方の後に実行される可能性がありますそれ、またはあなたが持っているもの(ただし、2つは常に互いに対して順番に実行されると確信していますので、少なくとも差は負にはなりません)。
第二に、マルチコア(またはマルチプロセッサ)システムでは、1つのrdtscが一方のコア/プロセッサで実行され、他方が別のコア/プロセッサで実行される場合があります。そのような場合、否定的な結果isは完全に可能です。
一般的に、Windowsで正確なタイマーが必要な場合は、QueryPerformanceCounter
を使用することをお勧めします。
rdtsc
の使用を本当に主張する場合は、完全にアセンブリ言語で記述された別のモジュールで実行するか(またはコンパイラ組み込み関数を使用して)、CまたはC++とリンクする必要があると思います。 64ビットモード用にそのコードを記述したことはありませんが、32ビットモードでは次のようになります。
xor eax, eax
cpuid
xor eax, eax
cpuid
xor eax, eax
cpuid
rdtsc
; save eax, edx
; code you're going to time goes here
xor eax, eax
cpuid
rdtsc
これは奇妙に見えることは知っていますが、実際には正しいです。 CPUIDを実行するのは、それがシリアル化命令であり(順不同で実行できない)、ユーザーモードで使用できるためです。インテルは、最初の実行が2番目とは異なる速度で実行できる/実行するという事実を文書化しているため、タイミングを開始する前に3回実行します(推奨されるのは3なので、3つです)。
次に、テスト対象のコード、シリアル化を強制する別のcpuid、およびコードが終了した後の時間を取得する最後のrdtscを実行します。
それに加えて、OSが提供するあらゆる手段を使用して、これらすべてを1つのプロセス/コアで実行するようにします。ほとんどの場合、コードのアライメントを強制することも必要です。アライメントを変更すると、実行速度がかなり大きく異なる場合があります。
最後に、それを何度も実行したい-そして、物事の途中で中断される可能性が常にあるので(たとえば、タスクスイッチ)、実行にかなりの時間がかかる可能性に備える必要があります。残りよりも長くなります。たとえば、1回につき40〜43クロックサイクルかかる5回の実行と、10000 +クロックサイクルかかる6回目です。明らかに、後者の場合、あなたは外れ値を捨てるだけです-それはあなたのコードからではありません。
まとめ:rdtsc命令自体の実行を管理することは、(ほとんど)心配する必要がほとんどありません。 rdtsc
から結果を取得する前に、実際には何でも意味するneedが必要です。
Windowsの場合、Visual StudioはRDTSC命令を実行して結果を返す便利な「コンパイラ組み込み関数」(つまり、コンパイラが理解する特別な関数)を提供します。
unsigned __int64 __rdtsc(void);