durationのtemplate parameterである、Periodは、コンパイル時定数である。Periodは、Rep1カウントあたりの経過秒数を、秒単位で表現する。つまり、ミリ秒の分解能を持っているならば、std::milli(typedef ratio<1, 1000> milli;)が使える。
しかし、現実のハードウェアタイマーは、そんな分かりやすい分解能を持っていない。しかも、プログラムが実行される環境のハードウェアが、実行時にしか分からない場合もある。一体どうするのか。
いや、そもそも、そんなに使いづらい、ハードウェアの限界の分解能を使うだろうか、ミリ秒だとか、マイクロ秒だとか、分かりやすい単位にした方がいいのではないか。
たとえば、HPETを有効にした私のWindows環境では、QueryPerformanceFrequencyは、14318180を返す。
これは、一秒間に、14318180回のカウントができると言うことを意味する。すなわち、1クロックは、約70ナノ秒である。
しかし、私の環境でHPETを無効にすると、QueryPerformanceFrequencyは、3579545を返す。これは、1クロックあたり、約279ナノ秒である。
HPETの有無は、実行時にしか分からない。ということは、通常のx86なWindows環境に置いては、マイクロ秒単位が、安全なのだろうか。まあ、そういうきりの良い数字なら、コンパイル時に指定できるし。
なんとなく、Windows環境で動くhigh_resolution_timerを書いてみたけど、std::ratioとstd::chrono::durationとstd::chrono::time_pointがないために、実行できない。
namespace std { namespace chrono {
struct Freq
{
LARGE_INTEGER freq ;
Freq()
{
QueryPerformanceFrequency( &freq ) ;
}
} ;
class high_resolution_clock
{
static Freq freq ;
public:
typedef std::int64_t rep;
typedef std::micro period;
typedef chrono::duration<rep, period> duration;
typedef chrono::time_point<high_resolution_clock , duration> time_point;
static const bool is_monotonic = false ;
static time_point now()
{
LARGE_INTEGER count ;
//if ( QueryPerformanceCounter( &count ) == 0 )
//{// エラーの場合、どうすればいいのだろう。規格は何も言及してない。
// count.QuadPart = 0 ;
// return time_point( duration.zero() ) ;
//}
QueryPerformanceCounter( &count ) ;
rep tick = static_cast(double(count.QuadPart) / (double(freq.freq.QuadPart) / 1000000.0) ) ;
//rep tick = count.QuadPart / (freq.freq.QuadPart / 1000000) ;
return time_point( duration(tick) ) ;
}
} ;
Freq high_resolution_clock::freq ;
} }
No comments:
Post a Comment