APIが値を返すのにかかった時間を計算したいと思います。このようなアクションにかかる時間はナノ秒のスペースです。 APIはC++クラス/関数なので、私はtimer.hを使用して同じものを計算しています:
#include <ctime>
#include <cstdio>
using namespace std;
int main(int argc, char** argv) {
clock_t start;
double diff;
start = clock();
diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
cout<<"printf: "<< diff <<'\n';
return 0;
}
上記のコードは、時間を秒単位で示しています。ナノ秒でより正確に同じようにするにはどうすればよいですか?
ループ内で関数を繰り返し実行することについて他の人が投稿したものは正しいです。
Linux(およびBSD)の場合、 clock_gettime() を使用します。
#include <sys/time.h>
int main()
{
timespec ts;
// clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}
QueryPerformanceCounter を使用するウィンドウの場合。そして、ここにもっとあります QPC
明らかに、既知の issue がいくつかのチップセットでQPCにあるため、それらのチップセットがないことを確認したい場合があります。また、一部のデュアルコアAMDでも 問題 が発生する場合があります。 sebbbiの2番目の投稿を参照してください。
QueryPerformanceCounter()とQueryPerformanceFrequency()は少し優れた解像度を提供しますが、異なる問題があります。たとえば、Windows XPでは、AMDデュアルコアドライバーパッケージを特別にインストールして問題を解決しない限り、すべてのAMD Athlon X2デュアルコアCPUは、いずれかのコアのPCを「ランダムに」返します(PCが少し後方にジャンプすることがあります)。同様の問題(p4 dual、p4 ht、core2 dual、core2 quad、phenom quad)を持つ他のdual +コアCPUには気づいていません。
2013/07/16を編集:
...通常、QueryPerformanceCounterとQueryPerformanceFrequencyは複数のプロセッサに合わせて調整されますが、BIOSまたはドライバのバグにより、スレッドがプロセッサ間を移動するときにこれらのルーチンが異なる値を返すことがあります...
ただし、このStackOverflowの回答 https://stackoverflow.com/a/4588605/34329 は、Win XPサービスパック2以降のMS OSでQPCが正常に動作することを示しています。
この記事では、Windows 7がプロセッサに不変のTSCがあるかどうかを判断し、ない場合は外部タイマーにフォールバックできることを示しています。 http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html プロセッサー間での同期は依然として問題です。
タイマーに関連するその他の詳細な読み取り:
詳細については、コメントを参照してください。
この新しい答えは、C++ 11の<chrono>
機能を使用しています。 <chrono>
の使用方法を示す他の回答がありますが、ここの他のいくつかの回答で言及されているRDTSC
機能で<chrono>
を使用する方法を示すものはありません。だから、<chrono>
でRDTSC
を使用する方法を示すと思いました。さらに、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
Assembly命令から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
ユニットがあります。
別の例として、反復ごとの平均クロックサイクル数を浮動小数点として出力したいので、doubleに基づいて、クロックのティックと同じ単位(コードではCycle
と呼ばれる)を持つ別の期間を作成します。
ループは、両側の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
を定義するためのマシンのクロック速度。そのレベルの精度では、システムコール clock() などではなく、CPUティックで推論する方が適切です。命令を実行するのに1ナノ秒以上かかる場合、ナノ秒の精度を持つことはほとんど不可能であることを忘れないでください。
それでも、 そのようなもの は始まりです:
CPUが最後に起動されてから渡された80x86 CPUクロックティックの数を取得する実際のコードは次のとおりです。 Pentium以上で動作します(386/486はサポートされていません)。このコードは実際にはMS Visual C++固有ですが、インラインアセンブリをサポートしている限り、おそらく他のコードに非常に簡単に移植できます。
inline __int64 GetCpuClocks()
{
// Counter
struct { int32 low, high; } counter;
// Use RDTSC instruction to get clocks count
__asm Push EAX
__asm Push EDX
__asm __emit 0fh __asm __emit 031h // RDTSC
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
// Return result
return *(__int64 *)(&counter);
}
この関数には、非常に高速であるという利点もあります。通常、実行にかかるCPUサイクルは50サイクル以下です。
タイミング図を使用 :
クロックカウントを真の経過時間に変換する必要がある場合は、チップのクロック速度で結果を割ります。 「定格」GHzは、チップの実際の速度とわずかに異なる可能性が高いことに注意してください。チップの真の速度を確認するには、いくつかの非常に優れたユーティリティまたはWin32呼び出し、QueryPerformanceFrequency()を使用できます。
これを正しく行うには、RDTSC
またはclock_gettime()
のいずれかの2つの方法のいずれかを使用できます。 2番目の方法は約2倍高速で、正しい絶対時間を提供できるという利点があります。 RDTSC
を正しく機能させるには、示されているとおりに使用する必要があることに注意してください(このページの他のコメントにはエラーがあり、特定のプロセッサーで不適切なタイミング値が生じる場合があります)
inline uint64_t rdtsc()
{
uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
"rdtsc\n"
: "=a" (lo), "=d" (hi)
:
: "%ebx", "%ecx" );
return (uint64_t)hi << 32 | lo;
}
およびclock_gettimeの場合:(マイクロ秒の解像度を任意に選択しました)
#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 Epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}
生成されるタイミングと値:
Absolute values:
rdtsc = 4571567254267600
clock_gettime = 1278605535506855
Processing time: (10000000 runs)
rdtsc = 2292547353
clock_gettime = 1031119636
目的の結果を得るために次を使用しています。
#include <time.h>
#include <iostream>
using namespace std;
int main (int argc, char** argv)
{
// reset the clock
timespec tS;
tS.tv_sec = 0;
tS.tv_nsec = 0;
clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
...
... <code to check for the time to be put here>
...
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;
return 0;
}
C++ 11 の場合、簡単なラッパーを次に示します。
#include <iostream>
#include <chrono>
class Timer
{
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count(); }
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
または、* nixのC++ 03の場合、
class Timer
{
public:
Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }
double elapsed() {
clock_gettime(CLOCK_REALTIME, &end_);
return end_.tv_sec - beg_.tv_sec +
(end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
}
void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }
private:
timespec beg_, end_;
};
使用例:
int main()
{
Timer tmr;
double t = tmr.elapsed();
std::cout << t << std::endl;
tmr.reset();
t = tmr.elapsed();
std::cout << t << std::endl;
return 0;
}
一般に、関数を呼び出すのにかかる時間を計るには、1回だけではなく、何回も呼び出す必要があります。関数を1回だけ呼び出し、実行に非常に短い時間がかかる場合でも、実際にタイマー関数を呼び出すオーバーヘッドがあり、どれだけ時間がかかるかわかりません。
たとえば、関数の実行に800 nsを要すると推定する場合、ループで1,000万回呼び出します(これには約8秒かかります)。合計時間を1,000万で割って、呼び出しごとの時間を取得します。
X86プロセッサで実行されているgccで次の関数を使用できます。
unsigned long long rdtsc()
{
#define rdtsc(low, high) \
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
unsigned int low, high;
rdtsc(low, high);
return ((ulonglong)high << 32) | low;
}
digital Mars C++の場合:
unsigned long long rdtsc()
{
_asm
{
rdtsc
}
}
チップ上の高性能タイマーを読み取ります。プロファイリングを行うときにこれを使用します。
Embedded Profiler(WindowsおよびLinuxで無料)を使用できます。これは、マルチプラットフォームタイマー(プロセッササイクルカウント)へのインターフェイスを持ち、1秒あたりのサイクル数を提供できます。
EProfilerTimer timer;
timer.Start();
... // Your code here
const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;
サイクルカウントの時間への再計算は、CPU周波数を動的に変更できる最新のプロセッサでは、おそらく危険な操作です。したがって、変換された時間が正しいことを確認するには、プロファイリングの前にプロセッサ周波数を修正する必要があります。
ここでBorlandコードを使用しているのは、ti_hundが時々負の数を与えるコードですが、タイミングはかなり良いです。
#include <dos.h>
void main()
{
struct time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...
// read the time here remove Hours and min if the time is in sec
gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d Hundreds:%d\n",t.ti_hour-Hour,
t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main
1秒未満の精度が必要な場合は、システム固有の拡張機能を使用する必要があり、オペレーティングシステムのドキュメントを確認する必要があります。 POSIXは gettimeofday でマイクロ秒までサポートしますが、コンピューターの周波数が1GHzを超えていないため、これ以上正確なものはありません。
Boostを使用している場合は、 boost :: posix_time を確認できます。
Brock Adamsのメソッドを使用して、単純なクラスを作成します。
int get_cpu_ticks()
{
LARGE_INTEGER ticks;
QueryPerformanceFrequency(&ticks);
return ticks.LowPart;
}
__int64 get_cpu_clocks()
{
struct { int32 low, high; } counter;
__asm cpuid
__asm Push EDX
__asm rdtsc
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
return *(__int64 *)(&counter);
}
class cbench
{
public:
cbench(const char *desc_in)
: desc(strdup(desc_in)), start(get_cpu_clocks()) { }
~cbench()
{
printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
if(desc) free(desc);
}
private:
char *desc;
__int64 start;
};
使用例:
int main()
{
{
cbench c("test");
... code ...
}
return 0;
}
結果:
テストにかかった時間:0.0002 ms
関数呼び出しのオーバーヘッドがいくらかありますが、それでも十分に高速である必要があります:)
アイデアがクイックテストに使用できる最小限の構造を持つことである場合、C++ファイルの#include
の直後の任意の場所にコピーアンドペーストを指定することをお勧めします。これは、私がAllmanスタイルのフォーマットを犠牲にしている唯一の例です。
構造体の最初の行の精度を簡単に調整できます。指定可能な値は、nanoseconds
、microseconds
、milliseconds
、seconds
、minutes
、またはhours
です。
#include <chrono>
struct MeasureTime
{
using precision = std::chrono::microseconds;
std::vector<std::chrono::steady_clock::time_point> times;
std::chrono::steady_clock::time_point oneLast;
void p() {
std::cout << "Mark "
<< times.size()/2
<< ": "
<< std::chrono::duration_cast<precision>(times.back() - oneLast).count()
<< std::endl;
}
void m() {
oneLast = times.back();
times.Push_back(std::chrono::steady_clock::now());
}
void t() {
m();
p();
m();
}
MeasureTime() {
times.Push_back(std::chrono::steady_clock::now());
}
};
MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...
Mark 1: 123
Mark 2: 32
Mark 3: 433234
後でレポートが必要な場合は、たとえば、間にあるコードも標準出力に書き込むためです。次に、次の関数を構造体に追加します(MeasureTime()の直前)。
void s() { // summary
int i = 0;
std::chrono::steady_clock::time_point tprev;
for(auto tcur : times)
{
if(i > 0)
{
std::cout << "Mark " << i << ": "
<< std::chrono::duration_cast<precision>(tprev - tcur).count()
<< std::endl;
}
tprev = tcur;
++i;
}
}
だから、あなただけを使用することができます:
MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();
これは、前と同じようにすべてのマークをリストしますが、他のコードが実行された後です。 m.s()
とm.t()
の両方を使用しないでください。
これがLinuxの場合、「gettimeofday」関数を使用しています。これは、エポック以降の秒とマイクロ秒を示す構造体を返します。次に、timersubを使用して2つを減算して時間の差を取得し、必要な時間の精度に変換します。ただし、ナノ秒を指定すると、関数 clock_gettime() が探しているもののように見えます。渡す構造体に秒とナノ秒の観点から時間を入れます。
うまく機能するNice Boost タイマーを次に示します。
//Stopwatch.hpp
#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP
//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>
class Stopwatch
{
public:
Stopwatch();
virtual ~Stopwatch();
void Restart();
std::uint64_t Get_elapsed_ns();
std::uint64_t Get_elapsed_us();
std::uint64_t Get_elapsed_ms();
std::uint64_t Get_elapsed_s();
private:
boost::chrono::high_resolution_clock::time_point _start_time;
};
#endif // STOPWATCH_HPP
//Stopwatch.cpp
#include "Stopwatch.hpp"
Stopwatch::Stopwatch():
_start_time(boost::chrono::high_resolution_clock::now()) {}
Stopwatch::~Stopwatch() {}
void Stopwatch::Restart()
{
_start_time = boost::chrono::high_resolution_clock::now();
}
std::uint64_t Stopwatch::Get_elapsed_ns()
{
boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(nano_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_us()
{
boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(micro_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_ms()
{
boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(milli_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_s()
{
boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(sec.count());
}
あれについてどう思う:
int iceu_system_GetTimeNow(long long int *res)
{
static struct timespec buffer;
//
#ifdef __CYGWIN__
if (clock_gettime(CLOCK_REALTIME, &buffer))
return 1;
#else
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
return 1;
#endif
*res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
return 0;
}