web-dev-qa-db-ja.com

経過時間を簡単に測定

私は time() を使って私のプログラムのさまざまな点を測定しようとしています。

私が理解していないのは、なぜ前後の値が同じであるのかということです。私はこれが私のプログラムをプロファイルする最良の方法ではないことを理解しています、私はただ何かがかかる時間を見たいです。

printf("**MyProgram::before time= %ld\n", time(NULL));

doSomthing();
doSomthingLong();

printf("**MyProgram::after time= %ld\n", time(NULL));

私が試してみました:

struct timeval diff, startTV, endTV;

gettimeofday(&startTV, NULL); 

doSomething();
doSomethingLong();

gettimeofday(&endTV, NULL); 

timersub(&endTV, &startTV, &diff);

printf("**time taken = %ld %ld\n", diff.tv_sec, diff.tv_usec);

**time taken = 0 26339の結果を読むにはどうすればいいですか?それは26,339ナノ秒= 26.3ミリ秒を意味しますか?

**time taken = 4 45025はどうですか、それは4秒と25ミリ秒を意味しますか?

253
hap497
#include <ctime>

void f() {
  using namespace std;
  clock_t begin = clock();

  code_to_time();

  clock_t end = clock();
  double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
}

time()関数は1秒以内の精度ですが、 CLOCKS_PER_SEC "clock"は1秒以内です。これは過度に単純化されていますが、簡単で持ち運び可能な測定です。

254
Roger Pate

時間計測メカニズムを抽象化し、各呼び出し可能オブジェクトの実行時間を最小追加コードで計測することができます。これは、単にタイマー構造体を介して呼び出すことで実現できます。さらに、コンパイル時にタイミングタイプをパラメータ化(ミリ秒、ナノ秒など)を指定できます。

Loki Astari によるレビューと可変テンプレートを使用するための提案に感謝します。This が転送された関数呼び出しの理由です。

#include <iostream>
#include <chrono>

template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    template<typename F, typename ...Args>
    static typename TimeT::rep execution(F&& func, Args&&... args)
    {
        auto start = std::chrono::steady_clock::now();
        std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
        auto duration = std::chrono::duration_cast< TimeT> 
                            (std::chrono::steady_clock::now() - start);
        return duration.count();
    }
};

int main() {
    std::cout << measure<>::execution(functor(dummy)) << std::endl;
}

Demo

Howard Hinnant のコメントによると、クロノシステムから脱出しないのがベストです。そのため、上記のクラスでは、追加の静的メソッドを提供することで、手動でcountを呼び出すことをユーザーに選択できます(C++ 14を参照)。

template<typename F, typename ...Args>
static auto duration(F&& func, Args&&... args)
{
    auto start = std::chrono::steady_clock::now();
    std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
    return std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now()-start);
} 

// call .count() manually later when needed (eg IO)
auto avg = (measure<>::duration(func) + measure<>::duration(func)) / 2.0;

そしてクライアントにとって最も有用であること

「I/Oの前に一連の期間を後処理したい(平均値など)」


完全な コードはここにあります 。クロノに基づいてベンチマークツールを構築しようという私の試みが記録されています ここ


C++ 17の std::invoke が利用可能であれば、executionのcallableの呼び出しは以下のようになります。

invoke(forward<decltype(func)>(func), forward<Args>(args)...);

メンバ関数へのポインタである呼び出し可能オブジェクトを提供します。

248
//***C++11 Style:***
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();

std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() <<std::endl;
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::nanoseconds> (end - begin).count() <<std::endl;
230
user3762106

あなたの質問からわかるように、あるコードの実行後の経過時間を知りたいようです。私はあなたが秒単位で結果を見て快適であろうと思います。もしそうなら、以下のようにdifftime()関数を使ってみてください。これで問題が解決することを願っています。

#include <time.h>
#include <stdio.h>

time_t start,end;
time (&start);
.
.
.
<your code>
.
.
.
time (&end);
double dif = difftime (end,start);
printf ("Elasped time is %.2lf seconds.", dif );
55
AKN

Windowsのみ: (この回答を投稿した後にLinuxタグが追加されました)

GetTickCount() を使用すると、システムが起動してから経過したミリ秒数を取得できます。

long int before = GetTickCount();

// Perform time-consuming operation

long int after = GetTickCount();
30
RvdK

time(NULL)関数は、1970年1月1日の00:00からの経過秒数を返します。そして、その関数はあなたのプログラムの中で異なる時に呼ばれるので、それはいつも違うでしょう C++の時 -

13
vodkhang

time(NULL)は、1970年1月1日から00:00に経過した秒数を返します( エポック )。 2つの値の違いは、処理に要した秒数です。

int t0 = time(NULL);
doSomthing();
doSomthingLong();
int t1 = time(NULL);

printf ("time = %d secs\n", t1 - t0);

getttimeofday()の場合と同様にマイクロ秒単位で現在の時刻を秒単位で返すtime()を使用すると、より良い結果を得ることができます。

12
philant
struct profiler
{
    std::string name;
    std::chrono::high_resolution_clock::time_point p;
    profiler(std::string const &n) :
        name(n), p(std::chrono::high_resolution_clock::now()) { }
    ~profiler()
    {
        using dura = std::chrono::duration<double>;
        auto d = std::chrono::high_resolution_clock::now() - p;
        std::cout << name << ": "
            << std::chrono::duration_cast<dura>(d).count()
            << std::endl;
    }
};

#define PROFILE_BLOCK(pbn) profiler _pfinstance(pbn)

使い方は以下のとおりです。

{
    PROFILE_BLOCK("Some time");
    // your code or function
}

これは範囲でRAIIに似ています

注これは私のものではありませんが、私はそれがここで関連していると思った

11
user6266295
#include<time.h> // for clock
#include<math.h> // for fmod
#include<cstdlib> //for system
#include <stdio.h> //for delay

using namespace std;

int main()
{


   clock_t t1,t2;

   t1=clock(); // first time capture

   // Now your time spanning loop or code goes here
   // i am first trying to display time elapsed every time loop runs

   int ddays=0; // d prefix is just to say that this variable will be used for display
   int dhh=0;
   int dmm=0;
   int dss=0;

   int loopcount = 1000 ; // just for demo your loop will be different of course

   for(float count=1;count<loopcount;count++)
   {

     t2=clock(); // we get the time now

     float difference= (((float)t2)-((float)t1)); // gives the time elapsed since t1 in milliseconds

    // now get the time elapsed in seconds

    float seconds = difference/1000; // float value of seconds
    if (seconds<(60*60*24)) // a day is not over
    {
        dss = fmod(seconds,60); // the remainder is seconds to be displayed
        float minutes= seconds/60;  // the total minutes in float
        dmm= fmod(minutes,60);  // the remainder are minutes to be displayed
        float hours= minutes/60; // the total hours in float
        dhh= hours;  // the hours to be displayed
        ddays=0;
    }
    else // we have reached the counting of days
    {
        float days = seconds/(24*60*60);
        ddays = (int)(days);
        float minutes= seconds/60;  // the total minutes in float
        dmm= fmod(minutes,60);  // the rmainder are minutes to be displayed
        float hours= minutes/60; // the total hours in float
        dhh= fmod (hours,24);  // the hours to be displayed

    }

    cout<<"Count Is : "<<count<<"Time Elapsed : "<<ddays<<" Days "<<dhh<<" hrs "<<dmm<<" mins "<<dss<<" secs";


    // the actual working code here,I have just put a delay function
    delay(1000);
    system("cls");

 } // end for loop

}// end of main 
9
Eskay

2番目のプログラムによって表示される値は、秒とマイクロ秒です。

0 26339 = 0.026'339 s =   26339 µs
4 45025 = 4.045'025 s = 4045025 µs
8
Didier Trosset
#include <ctime>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <sys/time.h>
using namespace std;
using namespace std::chrono;

void f1()
{
  high_resolution_clock::time_point t1 = high_resolution_clock::now();
  high_resolution_clock::time_point t2 = high_resolution_clock::now();
  double dif = duration_cast<nanoseconds>( t2 - t1 ).count();
  printf ("Elasped time is %lf nanoseconds.\n", dif );
}

void f2()
{
  timespec ts1,ts2;
  clock_gettime(CLOCK_REALTIME, &ts1);
  clock_gettime(CLOCK_REALTIME, &ts2);
  double dif = double( ts2.tv_nsec - ts1.tv_nsec );
  printf ("Elasped time is %lf nanoseconds.\n", dif );
}

void f3()
{
  struct timeval t1,t0;
  gettimeofday(&t0, 0);
  gettimeofday(&t1, 0);
  double dif = double( (t1.tv_usec-t0.tv_usec)*1000);
  printf ("Elasped time is %lf nanoseconds.\n", dif );
}
void f4()
{
  high_resolution_clock::time_point t1 , t2;
  double diff = 0;
  t1 = high_resolution_clock::now() ;
  for(int i = 1; i <= 10 ; i++)
  {
    t2 = high_resolution_clock::now() ;
    diff+= duration_cast<nanoseconds>( t2 - t1 ).count();
    t1 = t2;
  }
  printf ("high_resolution_clock:: Elasped time is %lf nanoseconds.\n", diff/10 );
}

void f5()
{
  timespec ts1,ts2;
  double diff = 0;
  clock_gettime(CLOCK_REALTIME, &ts1);
  for(int i = 1; i <= 10 ; i++)
  {
    clock_gettime(CLOCK_REALTIME, &ts2);
    diff+= double( ts2.tv_nsec - ts1.tv_nsec );
    ts1 = ts2;
  }
  printf ("clock_gettime:: Elasped time is %lf nanoseconds.\n", diff/10 );
}

void f6()
{
  struct timeval t1,t2;
  double diff = 0;
  gettimeofday(&t1, 0);
  for(int i = 1; i <= 10 ; i++)
  {
    gettimeofday(&t2, 0);
    diff+= double( (t2.tv_usec-t1.tv_usec)*1000);
    t1 = t2;
  }
  printf ("gettimeofday:: Elasped time is %lf nanoseconds.\n", diff/10 );
}

int main()
{
  //  f1();
  //  f2();
  //  f3();
  f6();
  f4();
  f5();
  return 0;
}
7
Akanksha Gupta

C++ std :: chronoには、クロスプラットフォームであるという明確な利点があります。ただし、POSIXのclock_gettime()と比較してもかなりのオーバーヘッドが発生します。私のLinuxマシンでは、すべてのstd::chrono::xxx_clock::now()フレーバーはだいたい同じです。

std::chrono::system_clock::now()
std::chrono::steady_clock::now()
std::chrono::high_resolution_clock::now()

POSIXのclock_gettime(CLOCK_MONOTONIC, &time)steady_clock::now()と同じであるべきですが、それはx3倍以上速いです!

完全を期すために、これが私のテストです。

#include <stdio.h>
#include <chrono>
#include <ctime>

void print_timediff(const char* prefix, const struct timespec& start, const 
struct timespec& end)
{
    double milliseconds = end.tv_nsec >= start.tv_nsec
                        ? (end.tv_nsec - start.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec) * 1e3
                        : (start.tv_nsec - end.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec - 1) * 1e3;
    printf("%s: %lf milliseconds\n", prefix, milliseconds);
}

int main()
{
    int i, n = 1000000;
    struct timespec start, end;

    // Test stopwatch
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i) {
        struct timespec dummy;
        clock_gettime(CLOCK_MONOTONIC, &dummy);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("clock_gettime", start, end);

    // Test chrono system_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::system_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::system_clock::now", start, end);

    // Test chrono steady_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::steady_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::steady_clock::now", start, end);

    // Test chrono high_resolution_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::high_resolution_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::high_resolution_clock::now", start, end);

    return 0;
}

そしてこれは、gcc7.2 -O3でコンパイルしたときの出力です。

clock_gettime: 24.484926 milliseconds
chrono::system_clock::now: 85.142108 milliseconds
chrono::steady_clock::now: 87.295347 milliseconds
chrono::high_resolution_clock::now: 84.437838 milliseconds
4
Alexey Polonsky

time(NULL)関数呼び出しは、epoc:1970年1月1日からの経過秒数を返します。おそらく、2つのタイムスタンプの差を取ることを意味します。

size_t start = time(NULL);
doSomthing();
doSomthingLong();

printf ("**MyProgram::time elapsed= %lds\n", time(NULL) - start);
3
wilhelmtell

これからわか​​るように、tv_secは経過秒数を格納し、tv_usecは別に経過したマイクロ秒を格納します。そしてそれらはお互いの変換ではありません。したがって、それらを適切な単位に変更し、合計経過時間を取得するために追加する必要があります。

struct timeval startTV, endTV;

gettimeofday(&startTV, NULL); 

doSomething();
doSomethingLong();

gettimeofday(&endTV, NULL); 

printf("**time taken in microseconds = %ld\n",
    (endTV.tv_sec * 1e6 + endTV.tv_usec - (startTV.tv_sec * 1e6 + startTV.tv_usec))
    );
2
Safar Ligal

内部的にはこの関数はシステムのクロックにアクセスするので、呼び出すたびに異なる値を返します。一般的に非関数型言語では、関数の中にはたくさんの副作用や隠れた状態がありますが、それは関数の名前と引数を見ただけでは見えないものです。

2
Mike Weller

Linuxでは、clock_gettime()が良い選択のひとつです。リアルタイムライブラリをリンクする必要があります(-lrt)。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

#define BILLION  1000000000L;

int main( int argc, char **argv )
  {
    struct timespec start, stop;
    double accum;

    if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) {
      perror( "clock gettime" );
      exit( EXIT_FAILURE );
    }

    system( argv[1] );

    if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) {
      perror( "clock gettime" );
      exit( EXIT_FAILURE );
    }

    accum = ( stop.tv_sec - start.tv_sec )
          + ( stop.tv_nsec - start.tv_nsec )
            / BILLION;
    printf( "%lf\n", accum );
    return( EXIT_SUCCESS );
  }
2
cloudrain21

他の人がすでに指摘したように、C標準ライブラリのtime()関数は1秒以上の分解能を持っていません。よりよい解像度を提供することができる唯一の完全に移植可能なC関数はclock()であるように見えます、しかしそれはwallclock時間よりむしろプロセッサ時間を測定します。自分自身をPOSIXプラットフォーム(Linuxなど)に限定することに満足している場合は、clock_gettime()関数が適しています。

C++ 11以降、さまざまなコンパイラやオペレーティングシステム間で移植性の高い形式で優れた解像度を提供する、はるかに優れた タイミング機能 が利用可能になりました。同様に、boost :: datetimeライブラリは移植性が高いはずの良い高解像度のタイミングクラスを提供する。

これらの機能のいずれかを使用する際の1つの課題は、システムクロックを照会することによって発生する時間遅延です。 clock_gettime()、 boost :: datetime 、std :: chronoを試してみると、この遅延は簡単に数マイクロ秒になることがあります。そのため、コードの任意の部分の期間を測定するときは、このサイズ程度の測定誤差があることを考慮するか、何らかの方法でそのゼロ誤差を修正するようにしてください。理想的には、関数にかかった時間の複数の測定値を集めて、多くの実行にわたってかかった平均または最大/最小時間を計算したいと思うかもしれません。

これらすべての移植性と統計収集の問題を解決するために、私は Github で利用可能なcxx-rtimersライブラリを開発しています。コードに埋め込まれた複数のタイマーから。 C++ 11コンパイラを使用している場合は、単に#include <rtimers/cxx11.hpp>を使用し、次のようなものを使用します。

void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensiveFunc");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

プログラムの終了時には、std :: cerrに書き込まれたタイミング統計の要約が表示されます。

Timer(expensiveFunc): <t> = 6.65289us, std = 3.91685us, 3.842us <= t <= 63.257us (n=731)

これは、平均時間、その標準偏差、上限と下限、およびこの関数が呼び出された回数を示します。

Linux固有のタイミング関数を使用したい場合は#include <rtimers/posix.hpp>を使用できます。あるいはBoostライブラリを使用しているが古いC++コンパイラを使用している場合は#include <rtimers/boost.hpp>を使用できます。これらのタイマークラスには、複数のスレッドから統計的なタイミング情報を収集できるバージョンもあります。システムクロックの2つの連続したクエリに関連するゼロエラーを推定することを可能にする方法もあります。

2
rwp

私は通常以下を使います。

#include <chrono>
#include <type_traits>

using perf_clock = std::conditional<
    std::chrono::high_resolution_clock::is_steady,
    std::chrono::high_resolution_clock,
    std::chrono::steady_clock
>::type;

using floating_seconds = std::chrono::duration<double>;

template<class F, class... Args>
floating_seconds run_test(Func&& func, Args&&... args)
{
   const auto t0 = perf_clock::now();
   std::forward<Func>(func)(std::forward<Args>(args)...);
   return floating_seconds(perf_clock::now() - t0);
} 

これは@ nikos-athanasiouが提案したものと同じですが、私は非定常クロックの使用を避け、持続時間として浮動秒数を使用します。

1
oliora

あなたのdoSomething関数はタイマーの粒度よりも早く起こるので、それらは同じです。試してください:

printf ("**MyProgram::before time= %ld\n", time(NULL));

for(i = 0; i < 1000; ++i) {
    doSomthing();
    doSomthingLong();
}

printf ("**MyProgram::after time= %ld\n", time(NULL));
1
kibibu

両方の値が同じである理由は、あなたの long手続き はそれほど時間がかからないからです - 1秒未満です。これが問題であることを確認するために、関数の最後に長いループ(for(int i = 0; i <100000000; i ++);)を追加してみてください。

上記のことが当てはまる場合は、より正確に時間を測定するために別のシステム関数を見つける必要があります(私はあなたがLinuxで作業していることを理解しているので、関数名であなたを助けることはできません)。私はLinuxにGetTickCount()に似た機能があると確信しています、あなたはそれを見つける必要があります。

1
David Božjak

ライブラリ内の個々の関数の実行時間を測定する必要がありました。私はすべての関数のすべての呼び出しを時間測定関数でラップする必要はありませんでした。それは呼び出しスタックを醜く深くするためです。私はまた、関数が早く終了したり例外をスローしたりする可能性があるときに煩雑になるので、すべての関数の上下にタイマーコードを入れたくありませんでした。だから私がやろうとしたのは時間を計るためにそれ自身の寿命を使うタイマーを作ることでした。

このようにして、問題のコードブロックの始めにこれらのオブジェクトの1つ(実際には関数または任意のスコープ)をインスタンス化し、それからインスタンスデストラクタが経過した時間を測定できるようにすることで、コードブロックの時間を測定できます。インスタンスが範囲外になったときの構築あなたは完全な例を見つけることができます ここ しかし、構造体は非常に単純です:

template <typename clock_t = std::chrono::steady_clock>
struct scoped_timer {
  using duration_t = typename clock_t::duration;
  const std::function<void(const duration_t&)> callback;
  const std::chrono::time_point<clock_t> start;

  scoped_timer(const std::function<void(const duration_t&)>& finished_callback) :
      callback(finished_callback), start(clock_t::now()) { }
  scoped_timer(std::function<void(const duration_t&)>&& finished_callback) :
      callback(finished_callback), start(clock_t::now()) { }
  ~scoped_timer() { callback(clock_t::now() - start); }
};

タイミング情報を使って何かをすることができるように(それを印刷するか、またはそれを保存するかなど)、構造体は提供されたファンクターにコールバックします。さらに複雑なことをする必要がある場合は、std::bindstd::placeholdersと一緒に使用して、より多くの引数を持つ関数をコールバックすることもできます。

これを使用する簡単な例を示します。

void test(bool should_throw) {
  scoped_timer<> t([](const scoped_timer<>::duration_t& elapsed) {
    auto e = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(elapsed).count();
    std::cout << "took " << e << "ms" << std::endl;
  });

  std::this_thread::sleep_for(std::chrono::seconds(1));

  if (should_throw)
    throw nullptr;

  std::this_thread::sleep_for(std::chrono::seconds(1));
}

もっと慎重にやりたい場合は、newdeleteを使って明示的にタイマーを開始したり停止したりできます。

0
Kevin Kreiser

OPの答えに/ 3つの具体的な質問。

"理解できないのは、前後の値が同じなのはなぜですか? "

最初の質問 およびサンプルコードは、time()の分解能が1秒であることを示しているので、答えは2つの関数が1秒以内に実行されることです。 しかし 時折、(明らかに非論理的に) 1秒に通知されます 2つのタイマーマークが1秒の境界をまたいでいる場合。

次の例では、この構造体を埋めるgettimeofday()を使用しています

struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};

2番目の質問 asks: "**time taken = 0 26339の結果はどうやって読むのですか?それは26,339ナノ秒= 26.3ミリ秒を意味しますか?"

私の2番目の答えは、所要時間が0秒と26339マイクロ秒、つまり0.026339秒であることです。これは、最初の例が1秒未満で実行されることを示しています。

3番目の質問 asks: "**time taken = 4 45025はどうですか、それは4秒と25ミリ秒を意味しますか?"

私の3番目の答えは、所要時間が4秒と45025マイクロ秒、つまり4.045025秒であるということです。

0
Weather Vane