C ++を使用してナノ秒単位で時間を提供するタイマー関数


101

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

上記のコードは秒単位で時間を与えます。どのようにしてナノ秒単位で、より正確に同じを得ますか?


上記のコードは秒単位で計算し、ナノ秒単位で答えを取得したい...
gagneet 2008年

良い回答を得るためには、プラットフォームを質問(およびできればタイトルも)に追加する必要があります。
Patrick Johnmeyer、2008年

時間を取得することに加えて、マイクロベンチマーク(非常に複雑)の問題を調べる必要があります。1回の実行で最初と最後の時間を取得するだけでは、十分な精度が得られない可能性があります。
Blaisorblade、2012

@Blaisorblade:特に、私のテストの一部で、clock()思ったほど高速ではないことがわかりました。
Mooing Duck、2012

回答:


83

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

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デュアルコアドライバーパッケージを特別にインストールして問題を修正しない限り、すべてのAMD Athlon X2デュアルコアCPUがいずれかのコアのPCを「ランダムに」返します(PCが少し後方にジャンプすることがあります)。他のデュアル+コアCPUで同様の問題が発生することはありません(p4デュアル、p4 ht、core2デュアル、core2クワッド、phenomクワッド)。

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には、Win XP Service Pack 2以降のQPCはすべてのMS OSで正常に動作するはずであると記載されています

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

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

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


1
古いデュアルXeon PCでTSCクロックスキューを見たことがありますが、C1クロックランピングが有効になっているAthlon X2ほどではありません。C1クロックランピングでは、HLT命令を実行するとクロックが遅くなり、アイドルコアのTSCがアクティブコアよりもゆっくりとインクリメントします。
bk1e 2008年

6
CLOCK_MONOTONICは、入手可能なLinuxのバージョンで動作します。
バーナード

1
@バーナード-私が最後にこれを見たので、それは新しく追加されなければなりません。ヘッドアップをありがとう。
悲しむ

3
実際、CLOCK_MONOTONIC_RAWNTPで調整されていないハードウェア時間を取得するには、使用可能な場合はを使用する必要があります。

ここで説明したように、QPCの正しい実装はTSCカウンターを使用しません、少なくともそれが信頼できないことがわかっている場合:stackoverflow.com/q/510462/53974
Blaisorblade

69

この新しい答えはC ++ 11の<chrono>機能を使用しています。を使用する方法を示す他の回答がありますが<chrono>、それらのどれも、ここで他のいくつかの回答で言及され<chrono>ているRDTSC機能を使用する方法を示していません。だから私はでの使い方を示すと思いRDTSCました<chrono>。さらに私はあなたが急速に切り替えることができるようにあなたが時計にテストコードをテンプレート化する方法を説明しますRDTSCと、お使いのシステムに組み込まれている可能性に基づいてされるクロック施設(clock()clock_gettime()および/またはQueryPerformanceCounter

RDTSC命令はx86固有であることに注意してください。 QueryPerformanceCounterWindowsのみです。そしてclock_gettime()、POSIXのみです。私は2個の新しい時計を導入下:std::chrono::high_resolution_clockstd::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_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. テストする反復の数。この数を変更すると結果が大きく異なる場合は、おそらく反復回数を増やすか、テスト中に競合するプロセスのコンピューターを空にする必要があります。

5
「RDTSCはIntel専用」とは、x86アーキテクチャとその派生物を指しているのではないでしょうか。 AMD、Cyrix、Transmeta x86チップには命令があり、Intel RISCおよびARMプロセッサには命令がありません。
Ben Voigt

1
@BenVoigt:+1はい、あなたの修正はかなり正しいです、ありがとう。
ハワードヒナント2012年

1
CPUスロットリングはこれにどのように影響しますか?CPU負荷に基づいてクロック速度は変わりませんか?
Tejas Kale

@TejasKale:これは、「クロックを構築するには...」で始まる2つの連続する段落の回答で説明されています。通常、タイミングコードは、スレッドをブロックする作業を測定しません(ただし、可能です)。したがって、通常、CPUはスロットルしません。ただし、スリープ、ミューテックスロック、condition_variable待機などを含むコードを測定している場合、rdtscクロックは他の単位への変換が不正確になる可能性があります。クロックを簡単に変更および比較できるように測定値を設定することをお勧めします(この回答に示されています)。
ハワードヒナン2016

27

そのレベルの精度では、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()を使用できます。


情報をありがとう、これは便利です。時間を計算するためのCPUサイクルについては考えていませんでした。これは心に留めておくのに非常に良いポイントだと思います:-)
gagneet 2008年

4
QueryPerformanceFrequency()を使用してTSCカウントを経過時間に変換すると、機能しない場合があります。QueryPerformanceCounter()は、利用可能な場合、VistaでHPET(高精度イベントタイマー)を使用します。ユーザーがboot.iniに/ USEPMTIMERを追加した場合、ACPI電源管理タイマーを使用します。
bk1e 2008年

23

これを行うには正しく、あなたはどちらか、2つの方法のいずれかを使用して行くことができるRDTSCかとclock_gettime()。秒は約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

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

2
このコードを適用しようとすると、タイムスペックが定義されていない理由を最初にグーグルする必要があったので、私は反対票を投じました。それから私はPOSIXをググググしなければならなかった...そして私が理解したように、このコードは標準ライブラリにこだわる必要があるWindowsユーザーには関係ありません。
ダニエル・カッツ

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から


5

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

たとえば、関数の実行に800 nsかかると推定する場合は、ループで1000万回呼び出します(約8秒かかります)。コールごとの時間を取得するには、合計時間を1000万で割ります。


actualyy、私は特定の呼び出しのAPIのパフォーマンスを取得しようとしています。実行ごとに、異なる時間を与える可能性があります。これは、パフォーマンス向上のために作成したグラフに影響を与える可能性があります...したがって、ナノ秒単位の時間。しかし、そうです、これは素晴らしいアイデアです。検討します。
gagneet 2008年

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

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


2
これは便利です。実験に
Apple

1
ユーザーは高値と低値にどのような値を与えるべきですか?関数の本体の中にマクロを定義するのはなぜですか?また、おそらくlongdefからunsigned long longに変換されたulonglongは、標準型ではありません。私はこれを使用したいのですが、私はわからない方法だ;)
ジョセフ・ガービンに

1
unsigned longは、Linuxで使用するのに適切なものではありません。64ビットLinuxではlongとlong longはどちらも64ビットであるため、代わりにintの使用を検討してください。
マリウス

3
現在、TSCカウンターは信頼性が低いことがよくあります。周波数が変更されると、多くのプロセッサで速度が変化し、異なるコア間で一貫性がないため、TSCが常に大きくなるとは限りません。
Blaisorblade、2012

1
@Marius:unsigned int内部型として使用して、コメントを実装しました。
Blaisorblade、2012

3

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

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


コードの移植性を維持したい場合は、boostライブラリが表示され、これをコードにバンドルできるかどうかが確認されます。感謝:-)
gagneet 2008年

3

私はここでボーランドのコードを使用しています。コード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

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 ms

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


3

マルチプロセッサタイマーへのインターフェイス(プロセッササイクルカウント)を備え、秒あたりのサイクル数を提供できる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周波数を動的に変更できる最新のプロセッサでは危険な操作になる可能性があります。したがって、変換された時刻が正しいことを確認するには、プロファイリングの前にプロセッサーの周波数を修正する必要があります。


2

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


clock_gettime()は今のところトリックを行うべきです。私の目的のために同じものを使用してみます...
gagneet 08年

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

2

以下は、うまく機能する素晴らしい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());
}

2

最小限のコピーと貼り付け構造+遅延使用

簡単なテストに使用できる最小限の構造体を使用するという考えの場合は、C ++ファイルのの直後にコピーして貼り付けることをお勧めします#include。これは私がオールマンスタイルのフォーマットを犠牲にした唯一の例です。

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


Ubuntu 16.04のOpenMPで完全に動作します。どうもありがとう、これがIMOの最良の答えになるはずです!
–ÍhorMé2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.