web-dev-qa-db-ja.com

C ++クロスプラットフォーム高解像度タイマー

私はC++で簡単なタイマーメカニズムを実装することを探しています。コードはWindowsとLinuxで動作するはずです。解像度は可能な限り正確でなければなりません(少なくともミリ秒の精度)。これは、時間の経過を単に追跡するために使用され、イベント駆動型のデザインを実装するためではありません。これを達成するための最良のツールは何ですか?

69

C++ 03の場合:

Boost.Timer は動作する可能性がありますが、C関数clockに依存するため、十分な解像度が得られない可能性があります。

Boost.Date_Timeには、以前にStack Overflowで推奨されていた ptime class が含まれています。 microsec_clock::local_timeおよびmicrosec_clock::universal_timeのドキュメントを参照してください。ただし、「Win32システムはこのAPIを介してマイクロ秒の解像度を達成できないことが多い」という注意事項に注意してください。

STLsoft は、とりわけ、OS固有のAPIのシンクロスプラットフォーム(WindowsおよびLinux/Unix)C++ラッパーを提供します。その パフォーマンスライブラリ には、必要なことを行ういくつかのクラスがあります。 (クロスプラットフォームにするには、winstl名前空間とunixstl名前空間の両方に存在するperformance_counterのようなクラスを選択し、プラットフォームに一致する名前空間を使用します。)

C++ 11以降の場合

std::chronoライブラリには、この機能が組み込まれています。詳細については、@ HowardHinnantによる this answer を参照してください。

40
Josh Kelley

古い質問の回答を更新しました:

C++ 11では、次のものを使用して最高の解像度のタイマーに移植できます。

_#include <iostream>
#include <chrono>
#include "chrono_io"

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    auto t1 = Clock::now();
    auto t2 = Clock::now();
    std::cout << t2-t1 << '\n';
}
_

出力例:

_74 nanoseconds
_

「chrono_io」は、これらの新しいタイプのI/O問題を緩和する拡張機能であり、自由に利用できます here

また、_<chrono>_の実装がboostで利用可能です(まだトランクの先端にある可能性があり、リリースされているかどうかは不明です)。

更新

これは、後続の_std::chrono::high_resolution_clock_の呼び出しがVS11で数ミリ秒かかるという以下のBenのコメントに対応しています。以下は_<chrono>_互換の回避策です。ただし、Intelハードウェアでのみ機能するため、インラインアセンブリを実行する必要があります(これを行う構文はコンパイラによって異なります)。また、マシンのクロック速度をクロックに固定する必要があります。

_#include <chrono>

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2800000000>          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));
    }

private:

    static
    unsigned
    get_clock_speed()
    {
        int mib[] = {CTL_HW, HW_CPU_FREQ};
        const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
        unsigned freq;
        size_t freq_len = sizeof(freq);
        if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
            return 0;
        return freq;
    }

    static
    bool
    check_invariants()
    {
        static_assert(1 == period::num, "period must be 1/freq");
        assert(get_clock_speed() == period::den);
        static_assert(std::is_same<rep, duration::rep>::value,
                      "rep and duration::rep must be the same type");
        static_assert(std::is_same<period, duration::period>::value,
                      "period and duration::period must be the same type");
        static_assert(std::is_same<duration, time_point::duration>::value,
                      "duration and time_point::duration must be the same type");
        return true;
    }

    static const bool invariants;
};

const bool clock::invariants = clock::check_invariants();
_

そのため、ポータブルではありません。しかし、もしあなたがあなた自身のIntelハードウェア上で高解像度のクロックを試してみたいのなら、それはこれより良くなることはありません。ただし、今日のクロック速度は動的に変化する可能性があります(実際にはコンパイル時の定数ではありません)。また、マルチプロセッサマシンを使用すると、異なるプロセッサからタイムスタンプを取得することもできます。それでも、私のハードウェアでの実験はかなりうまくいきます。ミリ秒の解像度に固執している場合、これは回避策になる可能性があります。

このクロックには、CPUのクロック速度の観点からの期間があります(報告したとおり)。つまり私にとって、このクロックは1/2,800,000,000秒ごとに1回刻みます。必要に応じて、これをナノ秒に変換できます(たとえば)。

_using std::chrono::nanoseconds;
using std::chrono::duration_cast;
auto t0 = clock::now();
auto t1 = clock::now();
nanoseconds ns = duration_cast<nanoseconds>(t1-t0);
_

変換により、CPUサイクルの端数が切り捨てられてナノ秒が形成されます。他の丸めモードも可能ですが、それは別のトピックです。

私にとっては、これは18クロックティックという短い期間を返しますが、これは6ナノ秒に切り捨てられます。

上記のクロックに「不変チェック」を追加しました。最も重要なのは、_clock::period_がマシンにとって正しいことをチェックすることです。繰り返しになりますが、これは移植可能なコードではありませんが、このクロックを使用している場合は、すでにコミットしています。ここに示すプライベートget_clock_speed()関数は、OS Xで最大CPU周波数を取得します。これは、_clock::period_の定数分母と同じ数でなければなりません。

これを追加すると、このコードを新しいマシンに移植し、_clock::period_を新しいマシンの速度に更新することを忘れた場合のデバッグ時間が少し短縮されます。すべてのチェックは、コンパイル時またはプログラムの起動時に行われます。したがって、clock::now()のパフォーマンスにはほとんど影響しません。

140
Howard Hinnant

マシューウィルソンSTLSoftライブラリ プラグアンドプレイできるように、一致するインターフェイスを備えたいくつかのタイマータイプを提供します。提供されるものの中には、低コストで低解像度のタイマーや、高解像度で高コストのタイマーがあります。スレッド前の時間を測定したり、プロセスごとの時間を測定したり、経過時間を測定したりするものもあります。

ドブ博士の記事 数年前の記事がありますが、WinSTLサブプロジェクトで定義されているWindowsの記事のみを網羅しています。 STLSoftは、UNIXSTLサブプロジェクトでUNIXタイマーも提供します。「PlatformSTL」を使用できます。これには、次のように、UNIXまたはWindowsの適切なものが含まれます。

#include <platformstl/performance/performance_counter.hpp>
#include <iostream>

int main()
{
    platformstl::performance_counter c;

    c.start();
    for(int i = 0; i < 1000000000; ++i);
    c.stop();

    std::cout << "time (s): " << c.get_seconds() << std::endl;
    std::cout << "time (ms): " << c.get_milliseconds() << std::endl;
    std::cout << "time (us): " << c.get_microseconds() << std::endl;
}

HTH

6
dcw

StlSoft オープンソースライブラリは、WindowsとLinuxプラットフォームの両方で非常に 良いタイマー を提供します。独自に実装する場合は、ソースをご覧ください。

5
Malte Clasen

ACEライブラリには、ポータブルな高解像度タイマーもあります。

高解像度タイマーのDoxygen:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html

5
Dan

これは最大の答えではありませんが、ゲーム開発サイトでの高解像度タイマーに関するいくつかの会話があります。

  1. http://www.gamedev.net/topic/374327-timing-is-everything/
  2. http://www.gamedev.net/topic/471804-high-resolution-timer/
  3. http://www.gamedev.net/topic/40600-high-resolution-timing-in-games/

そのためにはboost :: posix_timeライブラリを強くお勧めします。マイクロ秒までのさまざまな解像度のタイマーをサポートしています

4
Maciek

SDL2 には、優れたクロスプラットフォームの高解像度タイマーがあります。ただし、サブミリ秒の精度が必要な場合は、非常に小さなクロスプラットフォームタイマーライブラリ here を作成しました。 C++ 03とC++ 11/C++の上位バージョンの両方と互換性があります。

3
metamorphosis

私はこれをクローズドソースの社内ソリューションとして数回実装しました....これはすべて、ネイティブのWindows高解像度タイマーと#ifdefstruct timevalを参照)を使用したLinuxカーネルタイマーに関連するman timeraddソリューションに頼っていますハンド。

これを抽象化することができ、いくつかのオープンソースプロジェクトがそれを行いました-私が最後に見たのは CoinORクラスCoinTimer =しかし、確かにそれらはもっとあります。

3

STLSoft には パフォーマンスライブラリ があります。これには、UNIXとWindowsの両方で機能するタイマークラスのセットが含まれています。

2
JamieH

C++ライブラリの質問に対する最初の答えは、通常BOOSTです。 http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm 。これはあなたが望むことをしますか?おそらくそうではありませんが、それは始まりです。

問題は、移植性が必要であり、タイマー機能はOSで普遍的ではないということです。

2
jmucchiello

あなたの要件についてはわかりません。時間間隔を計算したい場合は、以下のスレッドを参照してください

Cプログラムの経過時間をミリ秒で計算

1
Satbir

ここではパーティーに遅れていますが、私はまだc ++ 11にアップグレードできないレガシーコードベースで作業しています。私たちのチームの誰もc ++に精通していないため、STLなどのライブラリを追加することは困難です(他の人が展開の問題について提起した潜在的な懸念に加えて)。必要最低限​​の標準システムライブラリを超えることなく、単独で動作できる非常にシンプルなクロスプラットフォームタイマーが本当に必要でした。私が見つけたものは次のとおりです。

http://www.songho.ca/misc/timer/timer.html

ここでソース全体を再投稿して、サイトが死んだ場合に失われないようにします。

    //////////////////////////////////////////////////////////////////////////////
// Timer.cpp
// =========
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#include "Timer.h"
#include <stdlib.h>

///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Timer::Timer()
{
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceFrequency(&frequency);
    startCount.QuadPart = 0;
    endCount.QuadPart = 0;
#else
    startCount.tv_sec = startCount.tv_usec = 0;
    endCount.tv_sec = endCount.tv_usec = 0;
#endif

    stopped = 0;
    startTimeInMicroSec = 0;
    endTimeInMicroSec = 0;
}



///////////////////////////////////////////////////////////////////////////////
// distructor
///////////////////////////////////////////////////////////////////////////////
Timer::~Timer()
{
}



///////////////////////////////////////////////////////////////////////////////
// start timer.
// startCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::start()
{
    stopped = 0; // reset stop flag
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&startCount);
#else
    gettimeofday(&startCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// stop the timer.
// endCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::stop()
{
    stopped = 1; // set timer stopped flag

#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&endCount);
#else
    gettimeofday(&endCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// compute elapsed time in micro-second resolution.
// other getElapsedTime will call this first, then convert to correspond resolution.
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMicroSec()
{
#if defined(WIN32) || defined(_WIN32)
    if(!stopped)
        QueryPerformanceCounter(&endCount);

    startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
    endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
    if(!stopped)
        gettimeofday(&endCount, NULL);

    startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
    endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif

    return endTimeInMicroSec - startTimeInMicroSec;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMilliSec()
{
    return this->getElapsedTimeInMicroSec() * 0.001;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInSec()
{
    return this->getElapsedTimeInMicroSec() * 0.000001;
}



///////////////////////////////////////////////////////////////////////////////
// same as getElapsedTimeInSec()
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTime()
{
    return this->getElapsedTimeInSec();
}

ヘッダーファイル:

//////////////////////////////////////////////////////////////////////////////
// Timer.h
// =======
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#ifndef TIMER_H_DEF
#define TIMER_H_DEF

#if defined(WIN32) || defined(_WIN32)   // Windows system specific
#include <windows.h>
#else          // Unix based system specific
#include <sys/time.h>
#endif


class Timer
{
public:
    Timer();                                    // default constructor
    ~Timer();                                   // default destructor

    void   start();                             // start timer
    void   stop();                              // stop the timer
    double getElapsedTime();                    // get elapsed time in second
    double getElapsedTimeInSec();               // get elapsed time in second (same as getElapsedTime)
    double getElapsedTimeInMilliSec();          // get elapsed time in milli-second
    double getElapsedTimeInMicroSec();          // get elapsed time in micro-second


protected:


private:
    double startTimeInMicroSec;                 // starting time in micro-second
    double endTimeInMicroSec;                   // ending time in micro-second
    int    stopped;                             // stop flag 
#if defined(WIN32) || defined(_WIN32)
    LARGE_INTEGER frequency;                    // ticks per second
    LARGE_INTEGER startCount;                   //
    LARGE_INTEGER endCount;                     //
#else
    timeval startCount;                         //
    timeval endCount;                           //
#endif
};

#endif // TIMER_H_DEF
0
Patrick

プロジェクトでQtフレームワークを使用している場合、最良の解決策はおそらくQElapsedTimerを使用することです。

0
lpapp