105

API が値を返すのにかかった時間を計算したいと考えています。このようなアクションにかかる時間は、ナノ秒単位です。API は C++ クラス/関数であるため、timer.h を使用して同じものを計算しています。

  #include <ctime>
  #include <iostream>

  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;
  }

上記のコードは、時間を秒単位で示します。ナノ秒単位でより正確に同じ結果を得るにはどうすればよいですか?

4

17 に答える 17

87

関数をループで繰り返し実行することについて他の人が投稿したことは正しいです。

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の詳細があります

一部のチップセットでは QPC に既知の問題があるようです。そのため、それらのチップセットを使用していないことを確認してください。さらに、一部のデュアル コア AMD も問題を引き起こす可能性があります。sebbbi による 2 番目の投稿を参照してください。彼は次のように述べています。

QueryPerformanceCounter() と QueryPerformanceFrequency() を使用すると、解像度が少し向上しますが、別の問題があります。たとえば、Windows XP では、すべての AMD Athlon X2 デュアル コア CPU は、AMD デュアル コア ドライバー パッケージを特別にインストールして問題を解決しない限り、いずれかのコアの PC を「ランダムに」返します (PC が少し後方にジャンプすることがあります)。他のデュアル+コア CPU で同様の問題が発生したことは確認されていません (p4 デュアル、p4 ht、core2 デュアル、core2 クワッド、フェノム クワッド)。

編集 2013/07/16:

http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspxに記載されているように、特定の状況下での QPC の有効性についていくつかの論争があるようです。

...QueryPerformanceCounter と QueryPerformanceFrequency は通常、複数のプロセッサに合わせて調整されますが、BIOS またはドライバのバグにより、スレッドがあるプロセッサから別のプロセッサに移動すると、これらのルーチンが異なる値を返す可能性があります...

ただし、この StackOverflow の回答https://stackoverflow.com/a/4588605/34329では、QPC は Win XP サービス パック 2 以降のどの MS OS でも正常に動作するはずであると述べています。

この記事では、Windows 7 がプロセッサに不変の TSC があるかどうかを判断し、そうでない場合は外部タイマーにフォールバックできることを示しています。http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.htmlプロセッサ間の同期は依然として問題です。

タイマーに関連するその他の優れた読み物:

詳細については、コメントを参照してください。

于 2008-11-08T21:29:18.107 に答える
71

この新しい回答は 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_clockstd::chrono::system_clockは、C++11 を仮定できる場合、クロスプラットフォームになりました。

まず、Intelrdtscアセンブリ命令から 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期間に変換して出力するために使用します。Cyclepicoseconds

このコードを使用するのは簡単です:

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>();
}

上記では、自作の を使用してテストを実行し、それらの結果を、システム提供の 2 つのクロックとx::clockを使用して比較し ます。私にとって、これは次のように出力されます:std::chrono::high_resolution_clockstd::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 つしかありません。

  1. 定義するための私のマシンのクロック速度x::clock
  2. テストする反復回数。この数を変更すると結果が大きく変わる場合は、おそらく反復回数を増やすか、テスト中に競合するプロセスからコンピューターを空にする必要があります。
于 2012-07-14T16:21:15.117 に答える
29

そのレベルの精度では、 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);

}

この関数には、非常に高速であるという利点もあります。通常、実行には 50 CPU サイクルしかかかりません。

タイミング図の使用:
クロック カウントを実際の経過時間に変換する必要がある場合は、結果をチップのクロック速度で割ります。「定格」GHz は、チップの実際の速度とは若干異なる可能性があることに注意してください。チップの実際の速度を確認するには、いくつかの非常に優れたユーティリティまたは Win32 呼び出し QueryPerformanceFrequency() を使用できます。

于 2008-11-08T18:37:55.817 に答える
23

RDTSCこれを正しく行うには、 withまたは withの 2 つの方法のいずれかを使用できますclock_gettime()。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
于 2010-07-08T16:16:07.527 に答える
22

望ましい結果を得るために以下を使用しています。

#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;
}
于 2008-11-12T10:50:10.877 に答える
8

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;
}

https://gist.github.com/gongzhitaao/7062087から

于 2013-10-19T22:04:23.473 に答える
5

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
   }
}

チップ上の高性能タイマーを読み取ります。プロファイリングを行うときにこれを使用します。

于 2008-11-09T03:06:45.167 に答える
5

一般に、関数の呼び出しにかかる時間を測定するには、1 回だけではなく、何回も実行する必要があります。関数を 1 回だけ呼び出し、実行にかかる時間が非常に短い場合でも、実際にタイマー関数を呼び出すオーバーヘッドがあり、どれくらい時間がかかるかわかりません。

たとえば、関数の実行に 800 ns かかると見積もった場合、ループ内で 1,000 万回呼び出します (約 8 秒かかります)。合計時間を 1,000 万で割ると、1 回の通話にかかる時間を取得できます。

于 2008-11-08T18:21:09.487 に答える
3

マルチプラットフォーム タイマー (プロセッサ サイクル カウント内) へのインターフェイスを備え、1 秒あたりのサイクル数を提供できるEmbedded Profiler (Windows および Linux では無料) を使用できます。

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 周波数を動的に変更できる最近のプロセッサでは、サイクル カウントを時間に合わせて再計算することは、おそらく危険な操作です。したがって、変換された時間が正しいことを確認するには、プロファイリングの前にプロセッサ周波数を修正する必要があります。

于 2012-11-09T09:35:15.557 に答える
3

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 ミリ秒

関数呼び出しのオーバーヘッドが多少ありますが、それでも十分に高速である必要があります:)

于 2012-06-28T10:10:25.043 に答える
3

ここで 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
于 2011-01-18T04:19:09.367 に答える
3

1 秒未満の精度が必要な場合は、システム固有の拡張機能を使用する必要があり、オペレーティング システムのドキュメントを確認する必要があります。POSIX はgettimeofdayで最大マイクロ秒をサポートしますが、コンピューターには 1 GHz を超える周波数がなかったため、これ以上正確なものはありません。

Boost を使用している場合は、boost::posix_timeを確認できます。

于 2008-11-08T18:56:53.687 に答える
2

あれについてどう思う:

    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;
    }
于 2012-08-29T11:25:47.727 に答える
2

最小限のコピー&ペースト構造体 + 怠惰な使用

簡単なテストに使用できる最小限の構造体を作成することが目的の場合は、C++ ファイルの's の直後にコピー アンド ペーストすることをお勧めします。#includeこれは、Allman スタイルのフォーマットを犠牲にした唯一の例です。

構造体の最初の行で精度を簡単に調整できます。可能な値は次のとおりです: nanosecondsmicrosecondsmillisecondssecondsminutesまたは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()

于 2016-02-10T10:42:50.570 に答える
2

これが Linux の場合、関数「gettimeofday」を使用しています。この関数は、エポックからの秒数とマイクロ秒数を示す構造体を返します。次に、timersub を使用して 2 つを減算して時間の差を取得し、必要な時間の精度に変換できます。ただし、ナノ秒を指定すると、関数clock_gettime()が探しているもののように見えます。渡す構造体に、秒単位およびナノ秒単位で時間を入れます。

于 2008-11-08T19:00:41.560 に答える