この新しい答えはC ++ 11の<chrono>
機能を使用しています。を使用する方法を示す他の回答がありますが<chrono>
、それらのどれも、ここで他のいくつかの回答で言及され<chrono>
ているRDTSC
機能を使用する方法を示していません。だから私はでの使い方を示すと思いRDTSC
ました<chrono>
。さらに私はあなたが急速に切り替えることができるようにあなたが時計にテストコードをテンプレート化する方法を説明しますRDTSC
と、お使いのシステムに組み込まれている可能性に基づいてされるクロック施設(clock()
、clock_gettime()
および/またはQueryPerformanceCounter
。
RDTSC
命令はx86固有であることに注意してください。 QueryPerformanceCounter
Windowsのみです。そしてclock_gettime()
、POSIXのみです。私は2個の新しい時計を導入下:std::chrono::high_resolution_clock
とstd::chrono::system_clock
あなたがC ++ 11を想定することができれば、今、クロスプラットフォームです。
まず、Intel rdtsc
アセンブリ命令からC ++ 11互換のクロックを作成する方法を次に示します。私はそれを呼びますx::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
このクロックが行うことは、CPUサイクルをカウントし、それを符号なし64ビット整数に格納することだけです。コンパイラのアセンブリ言語構文を微調整する必要があるかもしれません。または、コンパイラが代わりに使用できる組み込み関数を提供する場合があります(例:)now() {return __rdtsc();}
。
時計を構築するには、時計に表現(ストレージタイプ)を与える必要があります。マシンが異なる電力モードでクロック速度を変更する場合でも、コンパイル時間定数であるクロック周期も指定する必要があります。そして、それらから、これらの基本的な観点から、時計の「ネイティブ」時間の長さと時間ポイントを簡単に定義できます。
クロックティックの数を出力するだけの場合は、クロック周期に何を指定しても問題はありません。この定数は、クロック刻みの数をナノ秒などのリアルタイム単位に変換する場合にのみ機能します。そしてその場合、より正確にクロック速度を提供できるほど、ナノ秒への変換(ミリ秒など)がより正確になります。
以下は、使用方法を示すコード例ですx::clock
。実際に、同じ構文で多くの異なるクロックを使用する方法を示すために、コードをクロックにテンプレート化しました。この特定のテストは、ループの下で時間を計測したいものを実行したときのループオーバーヘッドを示しています。
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
このコードが最初に行うことは、結果を表示する「リアルタイム」ユニットを作成することです。私はピコ秒を選択しましたが、整数または浮動小数点ベースの任意の単位を選択できます。例として、std::chrono::nanoseconds
私が使用できたであろう既製のユニットがあります。
別の例として、反復ごとの平均クロックサイクル数を浮動小数点数として出力したいので、クロックのティックと同じ単位(Cycle
コードで呼び出される)と同じ単位を持つ別の期間をdoubleに基づいて作成します。
ループはclock::now()
どちらかの側への呼び出しでタイミングが取られます。この関数から返されるタイプに名前を付けたい場合は、次のようになります。
typename clock::time_point t0 = clock::now();
(x::clock
例で明確に示されているように、システム提供のクロックにも当てはまります)。
浮動小数点クロック刻みで期間を取得するには、単に2つの時間ポイントを減算し、反復ごとの値を取得するには、その期間を反復回数で割ります。
count()
メンバー関数を使用して、任意の期間のカウントを取得できます。これは内部表現を返します。最後にstd::chrono::duration_cast
、期間Cycle
を期間に変換しpicoseconds
て出力します。
このコードを使用するのは簡単です:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
上記では、自家製のを使用してテストを実行し、x::clock
それらの結果をシステム提供のクロックの2つを使用して比較 std::chrono::high_resolution_clock
しstd::chrono::system_clock
ます。私にとってこれは印刷されます:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
これは、反復ごとのティックがクロックごとに大きく異なるため、これらのクロックのそれぞれに異なるティック周期があることを示しています。ただし、既知の時間単位(ピコ秒など)に変換すると、各クロックでほぼ同じ結果が得られます(走行距離は異なる場合があります)。
私のコードに「マジック変換定数」が完全に含まれていないことに注意してください。実際、例全体では2つのマジック番号しかありません。
- を定義するために私のマシンのクロック速度
x::clock
。
- テストする反復の数。この数を変更すると結果が大きく異なる場合は、おそらく反復回数を増やすか、テスト中に競合するプロセスのコンピューターを空にする必要があります。