私はC++で簡単なタイマーメカニズムを実装することを探しています。コードはWindowsとLinuxで動作するはずです。解像度は可能な限り正確でなければなりません(少なくともミリ秒の精度)。これは、時間の経過を単に追跡するために使用され、イベント駆動型のデザインを実装するためではありません。これを達成するための最良のツールは何ですか?
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 を参照してください。
古い質問の回答を更新しました:
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()
のパフォーマンスにはほとんど影響しません。
マシューウィルソン の 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
ACEライブラリには、ポータブルな高解像度タイマーもあります。
高解像度タイマーのDoxygen:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html
これは最大の答えではありませんが、ゲーム開発サイトでの高解像度タイマーに関するいくつかの会話があります。
そのためにはboost :: posix_timeライブラリを強くお勧めします。マイクロ秒までのさまざまな解像度のタイマーをサポートしています
私はこれをクローズドソースの社内ソリューションとして数回実装しました....これはすべて、ネイティブのWindows高解像度タイマーと#ifdef
(struct timeval
を参照)を使用したLinuxカーネルタイマーに関連するman timeradd
ソリューションに頼っていますハンド。
これを抽象化することができ、いくつかのオープンソースプロジェクトがそれを行いました-私が最後に見たのは CoinORクラスCoinTimer =しかし、確かにそれらはもっとあります。
STLSoft には パフォーマンスライブラリ があります。これには、UNIXとWindowsの両方で機能するタイマークラスのセットが含まれています。
C++ライブラリの質問に対する最初の答えは、通常BOOSTです。 http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm 。これはあなたが望むことをしますか?おそらくそうではありませんが、それは始まりです。
問題は、移植性が必要であり、タイマー機能はOSで普遍的ではないということです。
あなたの要件についてはわかりません。時間間隔を計算したい場合は、以下のスレッドを参照してください
ここではパーティーに遅れていますが、私はまだ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
プロジェクトでQtフレームワークを使用している場合、最良の解決策はおそらくQElapsedTimerを使用することです。