- 処理時間測定や、ゲームのフレーム管理などのために、各プラットフォームにおいてマイクロ秒オーダーで時間測定する手段を知りたい。
Windows
調べるとよく出てくる GetTickCount()
や timeGetTime()
はミリ秒オーダー。マイクロ秒オーダーが欲しければ QueryPerformanceCounter()
を使う。
#include <windows.h>
// あらかじめタイマーの周波数を取得しておく
LARGE_INTEGER frequency;
QueryPerformanceFrequency( &frequency );
// 時間計測したい始点でカウンタを取得
LARGE_INTEGER start;
QueryPerformanceCounter( &start );
// ↑
// この間の時間を計測
// ↓
// 時間計測したい終点でカウンタを取得
LARGE_INTEGER end;
QueryPerformanceCounter( &end );
// カウンタの差分を周波数で割れば経過時間を秒で取得できる
LONGLONG span = end.QuadPart - start.QuadPart;
double sec = (double)span / (double)frequency.QuadPart;
// マイクロ秒が欲しければこんな?。数が大きいと溢れそうなので double 経由の方がいいのかも
LONGLONG usec = (span * 1000000L) / frequency.QuadPart;
QueryPerformanceCounter()
、QueryPerformanceFrequency()
は失敗すると 0 を返すが、MSDN によると WinXP 以降なら失敗する事はないらしいので、それ以降がターゲットならエラー処理は不要。
で、この QueryPerformanceCounter
、WinXp の頃はまんま CPU クロックを使ってたので、 省電力などでCPUクロックが変動すると計測結果が狂う 困った子だったみたいだけど、Vista以降(とその頃以降のチップセット)では他のクロックを使うようになって、問題なくなった模様。いろいろ大変だったのね。
あと、計測するなら同じスレッド上じゃないとダメ‥なのかな?。CPU クロックだった頃はダメだろうけど、この点が Vista 以降で解決したのか否かはイマイチ不明。そもそも個人的には別スレッド間で使う必要性を感じないので調べてない。
Android NDK
Java で開発する場合は System.nanoTime()
を使えば簡単に取得できる。
Android NDK では、UNIX系のOSであれば利用できる clock_gettime()
というのを使えば高精度の時刻が取得できるらしい。
#include <time.h>
// 現在時刻の取得
// 時間測定時は、OSの時刻設定の影響を受けない CLOCK_MONOTONIC で取得する
timespec ts;
clock_gettime( CLOCK_MONOTONIC, &ts );
// timespec には、秒単位の値(tv_sec)と、1秒未満のナノ秒単位の値(tv_nsec)が
// 別々に格納されるので、使いやすい単位にかけ合わせる。マイクロ秒なら以下
long long usec = ts.tv_sec * 1000000L + ts.tv_nsec / 1000;
大抵のサイトでは CLOCK_MONOTONIC
を使ってるようだけど、 CLOCK_MONOTONIC_RAW
というさらに無加工のクロックも用意されているので、計測用途にはたぶんそちらの方がよさそう(説明読んでも違いが出る状況がイマイチ分からん)。
iOS
iOS も UNIX ベースなので、Android NDK と同様に clock_gettime()
が利用可能。こちらでも CLOCK_MONOTONIC_RAW
でも問題なく利用できた。
[余談] μをuって書くのアリなん?
上の方でしれっとマイクロ秒の事を usec
とか書いたけど、マイクロ秒って変数とか関数とか作る時にいちいち microSec
とか書くのも長いし、msec
だとミリ秒と区別できないし、微妙に困ってたんよね。
で、μ と u って似てるし、usec でいいんじゃね? とかギリシャ人に殴られそうな事を思いついたものの、そんなソース見られたら恥ずかしいよねーとか思ってたら、何気に ISO 公認だったという。えっギリシャさんそれでいいの?。じゃあそうするわー。