時間情報の取得方法と扱い方
ここではC言語での時間情報の取得方法について説明していく、 時間情報には様々な種類があり、また環境により利用できる関数も変わってくる。 C言語の環境であればどこでも利用できる標準関数から始まり、POSIX環境、Windows環境で利用できるAPIも紹介する。
POSIX環境
引き続き、Linux をはじめとする POSIX 環境で使用できる方法を紹介する。
clock_gettime()
前回説明したように、
POSIX環境で古くから使われてきたgettimeofday()
は非推奨となり、この関数の利用が推奨されている。
プロトタイプ宣言は以下、C標準の関数ではないがヘッダは time.h である。
#include <time.h>
struct timespec {
time_t tv_sec; /* Seconds. */
long tv_nsec; /* Nanoseconds. */
};
int clock_getres(clockid_t clk_id, struct timespec *res);
int clock_gettime(clockid_t clk_id, struct timespec *tp);
こちらの関数で取得できる値の構造体はstruct timespec
である。
timespec_get()
で紹介した構造体だ。
秒単位の値とナノ秒単位の値で時間を表現する。
clock_getres
はclk_id
で示したクロックの精度をres
に格納する。
これまで紹介してきた関数は格納先の構造体から最大の精度は分かっても、実際の精度は不明だったが、
この関数によって取得することが可能になっている。
clock_gettime
はclk_id
で示したクロックの時間情報をtp
に格納する。
こちらが時間情報の関数となるが、clk_id
で様々なタイプの時間情報を取得することができる。
クロックの種別についての詳細は MAN 等を参照してもらうとして、抜粋しておく
- CLOCK_REALTIME
- システムで一意な実時間、システム時間の手動設定やNTPによる変更の影響を受ける
- CLOCK_REALTIME_COARSE
- (Linux特有)高速だが精度の低い CLOCK_REALTIME
- CLOCK_MONOTONIC
- ある時点からの単調増加する時間、システム時間の手動設定の影響を受けない、 NTP などの段階的な調整の影響は受ける
- CLOCK_MONOTONIC_COARSE
- (Linux特有)高速だが精度の低い CLOCK_MONOTONIC
- CLOCK_MONOTONIC_RAW
- (Linux特有)ある時点からの単調増加する時間、システム時間の手動設定の影響も NTPなどの段階的な調整の影響も受けない
- CLOCK_BOOTTIME
- (Linux特有)CLOCK_MONOTONICと同じだが、システムがサスペンドされている間の時間も含む
- CLOCK_PROCESS_CPUTIME_ID
- そのプロセスが消費する CPU 時間
- CLOCK_THREAD_CPUTIME_ID
- そのスレッドが消費する CPU 時間
実時刻を取得したいならCLOCK_REALTIME
を使えば良い、
時刻ではなく、経過実時間が必要な場合はCLOCK_MONOTONIC
を使用する、
消費した CPU 時間が必要な場合はCLOCK_PROCESS_CPUTIME_ID
する、
といったように使い分けることができる。
CLOCK_REALTIME
は実時刻なので取得した値を直接利用できる。
それ以外は、ある時点からの経過時間であり、
そのある時点は明確になっていないため、取得した値そのものにはあまり意味が無い。
2点以上で計測し、その差を利用する。
実験のため以下の様なコードを用意する。
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv) {
struct timespec ts;
printf("time(): %10ld\n", time(NULL));
clock_getres(CLOCK_REALTIME, &ts);
printf("res : %10ld.%09ld CLOCK_REALTIME\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_REALTIME, &ts);
printf("time: %10ld.%09ld CLOCK_REALTIME\n", ts.tv_sec, ts.tv_nsec);
#ifdef CLOCK_REALTIME_COARSE
clock_getres(CLOCK_REALTIME_COARSE, &ts);
printf("res : %10ld.%09ld CLOCK_REALTIME_COARSE\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_REALTIME_COARSE, &ts);
printf("time: %10ld.%09ld CLOCK_REALTIME_COARSE\n", ts.tv_sec, ts.tv_nsec);
#endif
clock_getres(CLOCK_MONOTONIC, &ts);
printf("res : %10ld.%09ld CLOCK_MONOTONIC\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("time: %10ld.%09ld CLOCK_MONOTONIC\n", ts.tv_sec, ts.tv_nsec);
#ifdef CLOCK_MONOTONIC_COARSE
clock_getres(CLOCK_MONOTONIC_COARSE, &ts);
printf("res : %10ld.%09ld CLOCK_MONOTONIC_COARSE\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
printf("time: %10ld.%09ld CLOCK_MONOTONIC_COARSE\n", ts.tv_sec, ts.tv_nsec);
#endif
#ifdef CLOCK_MONOTONIC_RAW
clock_getres(CLOCK_MONOTONIC_RAW, &ts);
printf("res : %10ld.%09ld CLOCK_MONOTONIC_RAW\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
printf("time: %10ld.%09ld CLOCK_MONOTONIC_RAW\n", ts.tv_sec, ts.tv_nsec);
#endif
#ifdef CLOCK_BOOTTIME
clock_getres(CLOCK_BOOTTIME, &ts);
printf("res : %10ld.%09ld CLOCK_BOOTTIME\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_BOOTTIME, &ts);
printf("time: %10ld.%09ld CLOCK_BOOTTIME\n", ts.tv_sec, ts.tv_nsec);
#endif
clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
printf("res : %10ld.%09ld CLOCK_PROCESS_CPUTIME_ID\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
printf("time: %10ld.%09ld CLOCK_PROCESS_CPUTIME_ID\n", ts.tv_sec, ts.tv_nsec);
clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts);
printf("res : %10ld.%09ld CLOCK_THREAD_CPUTIME_ID\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
printf("time: %10ld.%09ld CLOCK_THREAD_CPUTIME_ID\n", ts.tv_sec, ts.tv_nsec);
printf("clock(): %20.9f\n", (double)(clock()) / CLOCKS_PER_SEC);
return 0;
}
まずは、Ubuntu上で実行してみると
time(): 1446729673 res : 0.000000001 CLOCK_REALTIME time: 1446729673.346775809 CLOCK_REALTIME res : 0.004000000 CLOCK_REALTIME_COARSE time: 1446729673.345175591 CLOCK_REALTIME_COARSE res : 0.000000001 CLOCK_MONOTONIC time: 9617.304906812 CLOCK_MONOTONIC res : 0.004000000 CLOCK_MONOTONIC_COARSE time: 9617.303266756 CLOCK_MONOTONIC_COARSE res : 0.000000001 CLOCK_MONOTONIC_RAW time: 9616.862965705 CLOCK_MONOTONIC_RAW res : 0.000000001 CLOCK_BOOTTIME time: 9617.304961779 CLOCK_BOOTTIME res : 0.000000001 CLOCK_PROCESS_CPUTIME_ID time: 0.001448595 CLOCK_PROCESS_CPUTIME_ID res : 0.000000001 CLOCK_THREAD_CPUTIME_ID time: 0.001459506 CLOCK_THREAD_CPUTIME_ID clock(): 0.001466000
次に、Cygwin上で実行してみると
time(): 1446729681 res : 0.015600100 CLOCK_REALTIME time: 1446729681.142030400 CLOCK_REALTIME res : 0.000000383 CLOCK_MONOTONIC time: 13588.314030763 CLOCK_MONOTONIC res : 0.015600100 CLOCK_PROCESS_CPUTIME_ID time: 0.046800300 CLOCK_PROCESS_CPUTIME_ID res : 0.015600100 CLOCK_THREAD_CPUTIME_ID time: 0.046800300 CLOCK_THREAD_CPUTIME_ID clock(): 0.046000000
Linux 特有の値は Cygwin では取得できない。 時刻情報の精度という意味ではもちろんハードウェアに依存するが、 更にその上のOSがどのように扱うかによって変わってくるため、 取得できる精度に大きな違いがある。
また、上記例では参考までにtime()
と
clock()
の値を表示している。
CLOCK_REALTIME
の時のtv_sec
は、
time()
と同じ値になっている。
一方、CLOCK_PROCESS_CPUTIME_ID
で取得できる値は、
clock()
で取得できる値と同じになっていると分かるだろう。